diff --git a/.gitignore b/.gitignore index e96f31a..ba1943b 100644 --- a/.gitignore +++ b/.gitignore @@ -273,4 +273,6 @@ __pycache__/ /JobFiles/demo1ECommerceEvenLongerAgo.json /JobFiles/demo1ECommerceAWhileAgo.json -packages/* \ No newline at end of file +packages/* +/LicenseSign/LicenseFiles +/LicenseSign/Certificates diff --git a/AppDynamics.Dexter.Core.csproj b/AppDynamics.Dexter.Core.csproj index 9dac441..513fea8 100644 --- a/AppDynamics.Dexter.Core.csproj +++ b/AppDynamics.Dexter.Core.csproj @@ -56,13 +56,13 @@ - + - + - - - + + + @@ -96,6 +96,15 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + Always @@ -297,7 +306,7 @@ Always - + Always diff --git a/ChromeDriver/79/linux64/chromedriver b/ChromeDriver/81/linux64/chromedriver similarity index 58% rename from ChromeDriver/79/linux64/chromedriver rename to ChromeDriver/81/linux64/chromedriver index 6942445..c5fe1cb 100644 Binary files a/ChromeDriver/79/linux64/chromedriver and b/ChromeDriver/81/linux64/chromedriver differ diff --git a/ChromeDriver/79/mac64/chromedriver b/ChromeDriver/81/mac64/chromedriver similarity index 63% rename from ChromeDriver/79/mac64/chromedriver rename to ChromeDriver/81/mac64/chromedriver index d9bff50..13d2eb1 100644 Binary files a/ChromeDriver/79/mac64/chromedriver and b/ChromeDriver/81/mac64/chromedriver differ diff --git a/ChromeDriver/79/win32/chromedriver.exe b/ChromeDriver/81/win32/chromedriver.exe similarity index 53% rename from ChromeDriver/79/win32/chromedriver.exe rename to ChromeDriver/81/win32/chromedriver.exe index 244efd4..6909571 100644 Binary files a/ChromeDriver/79/win32/chromedriver.exe and b/ChromeDriver/81/win32/chromedriver.exe differ diff --git a/ControllerApi/ControllerApi.cs b/ControllerApi/ControllerApi.cs index e7a0b04..db64e93 100644 --- a/ControllerApi/ControllerApi.cs +++ b/ControllerApi/ControllerApi.cs @@ -1179,10 +1179,10 @@ public string GetDBCollectorsConfiguration(string controllerVersion) switch (controllerVersion) { case "4.4": - return this.apiGET("controller/rest/databases/collectors", "application/json", false); + return this.apiGET("controller/rest/databases/collectors", "application/json", true); case "4.5": - return this.apiGET("controller/databasesui/collectors", "application/json", false); + return this.apiGET("controller/databasesui/collectors", "application/json", true); default: return String.Empty; @@ -1276,29 +1276,77 @@ public string GetDBCustomMetrics(string controllerVersion) public string GetDBAllWaitStates(long dbCollectorID) { - return this.apiGET(String.Format("controller/databasesui/waitStateFiltering/getAllWaitStatesForDBServer/{0}", dbCollectorID), "application/json", true); + return this.apiGET( + String.Format("controller/databasesui/waitStateFiltering/getAllWaitStatesForDBServer/{0}", + dbCollectorID), + "application/json", + true); } #endregion #region DB data - public string GetDCurrentWaitStates(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat, long differenceInMinutes, string controllerVersion) + public string GetDCurrentWaitStates_4_4(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat, long differenceInMinutes) { - switch (controllerVersion) - { - case "4.4": - return this.apiGET(String.Format("controller/restui/databases/reports/waitReportData?dbId={0}&isCluster=false&timeRange=Custom_Time_Range|BETWEEN_TIMES|{2}|{1}|{3}&topNum=20", dbCollectorID, startTimeInUnixEpochFormat, endTimeInUnixEpochFormat, differenceInMinutes), "application/json", true); + return this.apiGET( + String.Format("controller/restui/databases/reports/waitReportData?dbId={0}&isCluster=false&timeRange=Custom_Time_Range|BETWEEN_TIMES|{2}|{1}|{3}&topNum=20", + dbCollectorID, + startTimeInUnixEpochFormat, + endTimeInUnixEpochFormat, + differenceInMinutes), + "application/json", + true); + } + public string GetDCurrentWaitStates_4_5(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat, long differenceInMinutes) + { + return this.apiGET( + String.Format("controller/databasesui/databases/reports/waitReportData?dbId={0}&isCluster=false&timeRange=Custom_Time_Range|BETWEEN_TIMES|{2}|{1}|{3}&topNum=20", + dbCollectorID, + startTimeInUnixEpochFormat, + endTimeInUnixEpochFormat, + differenceInMinutes), + "application/json", + true); + } - case "4.5": - return this.apiGET(String.Format("controller/databasesui/databases/reports/waitReportData?dbId={0}&isCluster=false&timeRange=Custom_Time_Range|BETWEEN_TIMES|{2}|{1}|{3}&topNum=20", dbCollectorID, startTimeInUnixEpochFormat, endTimeInUnixEpochFormat, differenceInMinutes), "application/json", true); + public string GetDCurrentWaitStates_20_4(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat, long differenceInMinutes) + { + return this.apiGET( + String.Format("controller/databasesui/databases/reports/waitReportData?dbConfigId=-1&dbServerId={0}&timeRange=Custom_Time_Range|BETWEEN_TIMES|{2}|{1}|{3}&topNum=20", + dbCollectorID, + startTimeInUnixEpochFormat, + endTimeInUnixEpochFormat, + differenceInMinutes), + "application/json", + true); + } - default: - return String.Empty; - } + + public string GetDBQueries_4_4(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) + { + string requestJSONTemplate = +@"{{ + ""cluster"": false, + ""serverId"": {0}, + ""field"": ""query-id"", + ""size"": 5000, + ""filterBy"": ""time"", + ""startTime"": {1}, + ""endTime"": {2}, + ""waitStateIds"": [], + ""useTimeBasedCorrelation"": false +}}"; + + string requestBody = String.Format(requestJSONTemplate, + dbCollectorID, + startTimeInUnixEpochFormat, + endTimeInUnixEpochFormat); + + return this.apiPOST("controller/restui/databases/queryListData", "application/json", requestBody, "application/json", true); } - public string GetDBQueries(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat, string controllerVersion) + public string GetDBQueries_4_5(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) { string requestJSONTemplate = @"{{ @@ -1318,20 +1366,33 @@ public string GetDBQueries(long dbCollectorID, long startTimeInUnixEpochFormat, startTimeInUnixEpochFormat, endTimeInUnixEpochFormat); - switch (controllerVersion) - { - case "4.4": - return this.apiPOST("controller/restui/databases/queryListData", "application/json", requestBody, "application/json", true); + return this.apiPOST("controller/databasesui/databases/queryListData", "application/json", requestBody, "application/json", true); + } - case "4.5": - return this.apiPOST("controller/databasesui/databases/queryListData", "application/json", requestBody, "application/json", true); + public string GetDBQueries_20_4(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) + { + string requestJSONTemplate = +@"{{ + ""dbConfigId"": -1, + ""dbServerId"": {0}, + ""field"": ""query-id"", + ""size"": 5000, + ""filterBy"": ""time"", + ""startTime"": {1}, + ""endTime"": {2}, + ""waitStateIds"": [], + ""useTimeBasedCorrelation"": false +}}"; - default: - return String.Empty; - } + string requestBody = String.Format(requestJSONTemplate, + dbCollectorID, + startTimeInUnixEpochFormat, + endTimeInUnixEpochFormat); + + return this.apiPOST("controller/databasesui/databases/queryListData", "application/json", requestBody, "application/json", true); } - public string GetDBClients(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat, string controllerVersion) + public string GetDBClients_4_4(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) { string requestJSONTemplate = @"{{ @@ -1349,20 +1410,53 @@ public string GetDBClients(long dbCollectorID, long startTimeInUnixEpochFormat, startTimeInUnixEpochFormat, endTimeInUnixEpochFormat); - switch (controllerVersion) - { - case "4.4": - return this.apiPOST("controller/restui/databases/clientListData", "application/json", requestBody, "application/json", true); + return this.apiPOST("controller/restui/databases/clientListData", "application/json", requestBody, "application/json", true); + } - case "4.5": - return this.apiPOST("controller/databasesui/databases/clientListData", "application/json", requestBody, "application/json", true); + public string GetDBClients_4_5(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) + { + string requestJSONTemplate = +@"{{ + ""serverId"": {0}, + ""cluster"": false, + ""field"": ""client-id"", + ""size"": 5000, + ""filterBy"": ""time"", + ""startTime"": {1}, + ""endTime"": {2} +}}"; - default: - return String.Empty; - } + string requestBody = String.Format(requestJSONTemplate, + dbCollectorID, + startTimeInUnixEpochFormat, + endTimeInUnixEpochFormat); + + return this.apiPOST("controller/databasesui/databases/clientListData", "application/json", requestBody, "application/json", true); + } + + public string GetDBClients_20_4(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) + { + string requestJSONTemplate = +@"{{ + ""dbConfigId"": -1, + ""dbServerId"": {0}, + ""field"": ""client-id"", + ""size"": 5000, + ""filterBy"": ""time"", + ""startTime"": {1}, + ""endTime"": {2} +}}"; + + string requestBody = String.Format(requestJSONTemplate, + dbCollectorID, + startTimeInUnixEpochFormat, + endTimeInUnixEpochFormat); + + return this.apiPOST("controller/databasesui/databases/clientListData", "application/json", requestBody, "application/json", true); } - public string GetDBSessions(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat, string controllerVersion) + + public string GetDBSessions_4_4(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) { string requestJSONTemplate = @"{{ @@ -1380,80 +1474,128 @@ public string GetDBSessions(long dbCollectorID, long startTimeInUnixEpochFormat, startTimeInUnixEpochFormat, endTimeInUnixEpochFormat); - switch (controllerVersion) - { - case "4.4": - return this.apiPOST("controller/restui/databases/queryListData", "application/json", requestBody, "application/json", true); + return this.apiPOST("controller/restui/databases/queryListData", "application/json", requestBody, "application/json", true); + } - case "4.5": - return this.apiPOST("controller/databasesui/databases/queryListData", "application/json", requestBody, "application/json", true); + public string GetDBSessions_4_5(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) + { + string requestJSONTemplate = +@"{{ + ""serverId"": {0}, + ""cluster"": false, + ""field"": ""session-id"", + ""size"": 5000, + ""filterBy"": ""time"", + ""startTime"": {1}, + ""endTime"": {2} +}}"; - default: - return String.Empty; - } + string requestBody = String.Format(requestJSONTemplate, + dbCollectorID, + startTimeInUnixEpochFormat, + endTimeInUnixEpochFormat); + + return this.apiPOST("controller/databasesui/databases/queryListData", "application/json", requestBody, "application/json", true); } - public string GetDBBlockingSessions(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat, long durationBetweenTimes, string controllerVersion) + public string GetDBSessions_20_4(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) { - switch (controllerVersion) - { - case "4.4": - return this.apiGET( - String.Format("controller/restui/databases/getBlockingTreeData?dbId={0}&isCluster=false&timerange=Custom_Time_Range.BETWEEN_TIMES.{1}.{2}.{3}", - dbCollectorID, - endTimeInUnixEpochFormat, - startTimeInUnixEpochFormat, - durationBetweenTimes), - "application/json", - true); + string requestJSONTemplate = +@"{{ + ""dbConfigId"": -1, + ""dbServerId"": {0}, + ""field"": ""session-id"", + ""size"": 5000, + ""filterBy"": ""time"", + ""startTime"": {1}, + ""endTime"": {2} +}}"; - case "4.5": - return this.apiGET( - String.Format("controller/databasesui/databases/getBlockingTreeData?dbId={0}&isCluster=false&timerange=Custom_Time_Range.BETWEEN_TIMES.{1}.{2}.{3}", - dbCollectorID, - endTimeInUnixEpochFormat, - startTimeInUnixEpochFormat, - durationBetweenTimes), - "application/json", - true); + string requestBody = String.Format(requestJSONTemplate, + dbCollectorID, + startTimeInUnixEpochFormat, + endTimeInUnixEpochFormat); - default: - return String.Empty; - } + return this.apiPOST("controller/databasesui/databases/queryListData", "application/json", requestBody, "application/json", true); } - public string GetDBBlockingSession(long dbCollectorID, long sessionID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat, long durationBetweenTimes, string controllerVersion) + public string GetDBBlockingSessions_4_4(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat, long durationBetweenTimes) { - switch (controllerVersion) - { - case "4.4": - return this.apiGET( - String.Format("controller/restui/databases/getBlockingTreeChildrenData?dbId={0}&isCluster=false&timerange=Custom_Time_Range.BETWEEN_TIMES.{2}.{3}.{4}&blockigSessionId={1}", - dbCollectorID, - sessionID, - endTimeInUnixEpochFormat, - startTimeInUnixEpochFormat, - durationBetweenTimes), - "application/json", - true); + return this.apiGET( + String.Format("controller/restui/databases/getBlockingTreeData?dbId={0}&isCluster=false&timerange=Custom_Time_Range.BETWEEN_TIMES.{1}.{2}.{3}", + dbCollectorID, + endTimeInUnixEpochFormat, + startTimeInUnixEpochFormat, + durationBetweenTimes), + "application/json", + true); + } - case "4.5": - return this.apiGET( - String.Format("controller/databasesui/databases/getBlockingTreeChildrenData?dbId={0}&isCluster=false&timerange=Custom_Time_Range.BETWEEN_TIMES.{2}.{3}.{4}&blockigSessionId={1}", - dbCollectorID, - sessionID, - endTimeInUnixEpochFormat, - startTimeInUnixEpochFormat, - durationBetweenTimes), - "application/json", - true); + public string GetDBBlockingSessions_4_5(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat, long durationBetweenTimes) + { + return this.apiGET( + String.Format("controller/databasesui/databases/getBlockingTreeData?dbId={0}&isCluster=false&timerange=Custom_Time_Range.BETWEEN_TIMES.{1}.{2}.{3}", + dbCollectorID, + endTimeInUnixEpochFormat, + startTimeInUnixEpochFormat, + durationBetweenTimes), + "application/json", + true); + } - default: - return String.Empty; - } + public string GetDBBlockingSessions_20_4(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat, long durationBetweenTimes) + { + return this.apiGET( + String.Format("controller/databasesui/databases/getBlockingTreeData?dbConfigId=-1&dbServerId={0}&timerange=Custom_Time_Range.BETWEEN_TIMES.{1}.{2}.{3}", + dbCollectorID, + endTimeInUnixEpochFormat, + startTimeInUnixEpochFormat, + durationBetweenTimes), + "application/json", + true); + } + + public string GetDBBlockingSession_4_4(long dbCollectorID, long sessionID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat, long durationBetweenTimes) + { + return this.apiGET( + String.Format("controller/restui/databases/getBlockingTreeChildrenData?dbId={0}&isCluster=false&timerange=Custom_Time_Range.BETWEEN_TIMES.{2}.{3}.{4}&blockigSessionId={1}", + dbCollectorID, + sessionID, + endTimeInUnixEpochFormat, + startTimeInUnixEpochFormat, + durationBetweenTimes), + "application/json", + true); + } + + public string GetDBBlockingSession_4_5(long dbCollectorID, long sessionID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat, long durationBetweenTimes) + { + return this.apiGET( + String.Format("controller/databasesui/databases/getBlockingTreeChildrenData?dbId={0}&isCluster=false&timerange=Custom_Time_Range.BETWEEN_TIMES.{2}.{3}.{4}&blockigSessionId={1}", + dbCollectorID, + sessionID, + endTimeInUnixEpochFormat, + startTimeInUnixEpochFormat, + durationBetweenTimes), + "application/json", + true); + } + + public string GetDBBlockingSession_20_4(long dbCollectorID, long sessionID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat, long durationBetweenTimes) + { + // blockig, really? Oh man. + return this.apiGET( + String.Format("controller/databasesui/databases/getBlockingTreeChildrenData?dbConfigId=-1&dbServerId={0}&startTime={3}&endTime={2}&blockingSessionId={1}&blockerQueryId=0", + dbCollectorID, + sessionID, + endTimeInUnixEpochFormat, + startTimeInUnixEpochFormat, + durationBetweenTimes), + "application/json", + true); } - public string GetDBDatabases(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat, string controllerVersion) + public string GetDBDatabases_4_4(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) { string requestJSONTemplate = @"{{ @@ -1471,20 +1613,52 @@ public string GetDBDatabases(long dbCollectorID, long startTimeInUnixEpochFormat startTimeInUnixEpochFormat, endTimeInUnixEpochFormat); - switch (controllerVersion) - { - case "4.4": - return this.apiPOST("controller/restui/databases/queryListData", "application/json", requestBody, "application/json", true); + return this.apiPOST("controller/restui/databases/queryListData", "application/json", requestBody, "application/json", true); + } - case "4.5": - return this.apiPOST("controller/databasesui/databases/queryListData", "application/json", requestBody, "application/json", true); + public string GetDBDatabases_4_5(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) + { + string requestJSONTemplate = +@"{{ + ""serverId"": {0}, + ""cluster"": false, + ""field"": ""schema-id"", + ""size"": 5000, + ""filterBy"": ""time"", + ""startTime"": {1}, + ""endTime"": {2} +}}"; - default: - return String.Empty; - } + string requestBody = String.Format(requestJSONTemplate, + dbCollectorID, + startTimeInUnixEpochFormat, + endTimeInUnixEpochFormat); + + return this.apiPOST("controller/databasesui/databases/queryListData", "application/json", requestBody, "application/json", true); + } + + public string GetDBDatabases_20_4(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) + { + string requestJSONTemplate = +@"{{ + ""dbConfigId"": -1, + ""dbServerId"": {0}, + ""field"": ""schema-id"", + ""size"": 5000, + ""filterBy"": ""time"", + ""startTime"": {1}, + ""endTime"": {2} +}}"; + + string requestBody = String.Format(requestJSONTemplate, + dbCollectorID, + startTimeInUnixEpochFormat, + endTimeInUnixEpochFormat); + + return this.apiPOST("controller/databasesui/databases/queryListData", "application/json", requestBody, "application/json", true); } - public string GetDBUsers(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat, string controllerVersion) + public string GetDBUsers_4_4(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) { string requestJSONTemplate = @"{{ @@ -1502,20 +1676,51 @@ public string GetDBUsers(long dbCollectorID, long startTimeInUnixEpochFormat, lo startTimeInUnixEpochFormat, endTimeInUnixEpochFormat); - switch (controllerVersion) - { - case "4.4": - return this.apiPOST("controller/restui/databases/queryListData", "application/json", requestBody, "application/json", true); + return this.apiPOST("controller/restui/databases/queryListData", "application/json", requestBody, "application/json", true); + } - case "4.5": - return this.apiPOST("controller/databasesui/databases/queryListData", "application/json", requestBody, "application/json", true); + public string GetDBUsers_4_5(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) + { + string requestJSONTemplate = +@"{{ + ""serverId"": {0}, + ""cluster"": false, + ""field"": ""db-user-id"", + ""size"": 5000, + ""filterBy"": ""time"", + ""startTime"": {1}, + ""endTime"": {2} +}}"; - default: - return String.Empty; - } + string requestBody = String.Format(requestJSONTemplate, + dbCollectorID, + startTimeInUnixEpochFormat, + endTimeInUnixEpochFormat); + + return this.apiPOST("controller/databasesui/databases/queryListData", "application/json", requestBody, "application/json", true); + } + public string GetDBUsers_20_4(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) + { + string requestJSONTemplate = +@"{{ + ""dbConfigId"": -1, + ""dbServerId"": {0}, + ""field"": ""db-user-id"", + ""size"": 5000, + ""filterBy"": ""time"", + ""startTime"": {1}, + ""endTime"": {2} +}}"; + + string requestBody = String.Format(requestJSONTemplate, + dbCollectorID, + startTimeInUnixEpochFormat, + endTimeInUnixEpochFormat); + + return this.apiPOST("controller/databasesui/databases/queryListData", "application/json", requestBody, "application/json", true); } - public string GetDBModules(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat, string controllerVersion) + public string GetDBModules_4_4(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) { string requestJSONTemplate = @"{{ @@ -1533,20 +1738,52 @@ public string GetDBModules(long dbCollectorID, long startTimeInUnixEpochFormat, startTimeInUnixEpochFormat, endTimeInUnixEpochFormat); - switch (controllerVersion) - { - case "4.4": - return this.apiPOST("controller/restui/databases/queryListData", "application/json", requestBody, "application/json", true); + return this.apiPOST("controller/restui/databases/queryListData", "application/json", requestBody, "application/json", true); + } - case "4.5": - return this.apiPOST("controller/databasesui/databases/queryListData", "application/json", requestBody, "application/json", true); + public string GetDBModules_4_5(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) + { + string requestJSONTemplate = +@"{{ + ""serverId"": {0}, + ""cluster"": false, + ""field"": ""module-id"", + ""size"": 5000, + ""filterBy"": ""time"", + ""startTime"": {1}, + ""endTime"": {2} +}}"; - default: - return String.Empty; - } + string requestBody = String.Format(requestJSONTemplate, + dbCollectorID, + startTimeInUnixEpochFormat, + endTimeInUnixEpochFormat); + + return this.apiPOST("controller/databasesui/databases/queryListData", "application/json", requestBody, "application/json", true); + } + + public string GetDBModules_20_4(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) + { + string requestJSONTemplate = +@"{{ + ""dbConfigId"": -1, + ""dbServerId"": {0}, + ""field"": ""module-id"", + ""size"": 5000, + ""filterBy"": ""time"", + ""startTime"": {1}, + ""endTime"": {2} +}}"; + + string requestBody = String.Format(requestJSONTemplate, + dbCollectorID, + startTimeInUnixEpochFormat, + endTimeInUnixEpochFormat); + + return this.apiPOST("controller/databasesui/databases/queryListData", "application/json", requestBody, "application/json", true); } - public string GetDBPrograms(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat, string controllerVersion) + public string GetDBPrograms_4_4(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) { string requestJSONTemplate = @"{{ @@ -1564,20 +1801,71 @@ public string GetDBPrograms(long dbCollectorID, long startTimeInUnixEpochFormat, startTimeInUnixEpochFormat, endTimeInUnixEpochFormat); - switch (controllerVersion) - { - case "4.4": - return this.apiPOST("controller/restui/databases/queryListData", "application/json", requestBody, "application/json", true); + return this.apiPOST("controller/restui/databases/queryListData", "application/json", requestBody, "application/json", true); + } - case "4.5": - return this.apiPOST("controller/databasesui/databases/queryListData", "application/json", requestBody, "application/json", true); + public string GetDBPrograms_4_5(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) + { + string requestJSONTemplate = +@"{{ + ""serverId"": {0}, + ""cluster"": false, + ""field"": ""program-id"", + ""size"": 5000, + ""filterBy"": ""time"", + ""startTime"": {1}, + ""endTime"": {2} +}}"; - default: - return String.Empty; - } + string requestBody = String.Format(requestJSONTemplate, + dbCollectorID, + startTimeInUnixEpochFormat, + endTimeInUnixEpochFormat); + + return this.apiPOST("controller/databasesui/databases/queryListData", "application/json", requestBody, "application/json", true); + } + + public string GetDBPrograms_20_4(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) + { + string requestJSONTemplate = +@"{{ + ""dbConfigId"": -1, + ""dbServerId"": {0}, + ""field"": ""program-id"", + ""size"": 5000, + ""filterBy"": ""time"", + ""startTime"": {1}, + ""endTime"": {2} +}}"; + + string requestBody = String.Format(requestJSONTemplate, + dbCollectorID, + startTimeInUnixEpochFormat, + endTimeInUnixEpochFormat); + + return this.apiPOST("controller/databasesui/databases/queryListData", "application/json", requestBody, "application/json", true); + } + + public string GetDBBusinessTransactions_20_4(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) + { + string requestJSONTemplate = +@"{{ + ""dbConfigId"": -1, + ""dbServerId"": {0}, + ""size"": 5000, + ""startTime"": {1}, + ""endTime"": {2} +}}"; + + string requestBody = String.Format(requestJSONTemplate, + dbCollectorID, + startTimeInUnixEpochFormat, + endTimeInUnixEpochFormat); + + return this.apiPOST("controller/databasesui/snapshot/getBTListViewData", "application/json", requestBody, "application/json", true); } - public string GetDBBusinessTransactions(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) + public string GetDBBusinessTransactions_Pre_20_4(long dbCollectorID, long startTimeInUnixEpochFormat, long endTimeInUnixEpochFormat) { string requestJSONTemplate = @"{{ diff --git a/DataObjects/CompareStatesConfiguration/CompareInput.cs b/DataObjects/CompareStatesConfiguration/CompareInput.cs index cd02494..f881c63 100644 --- a/DataObjects/CompareStatesConfiguration/CompareInput.cs +++ b/DataObjects/CompareStatesConfiguration/CompareInput.cs @@ -2,10 +2,14 @@ { public class CompareInput { + public CompareTimeRange TimeRange { get; set; } public bool DetectedEntities { get; set; } public bool Flowmaps { get; set; } public bool Metrics { get; set; } public bool Snapshots { get; set; } public bool Configuration { get; set; } + public bool UsersGroupsRolesPermissions { get; set; } + public bool Dashboards { get; set; } + public bool Licenses { get; set; } } } diff --git a/DataObjects/CompareStatesConfiguration/CompareOutput.cs b/DataObjects/CompareStatesConfiguration/CompareOutput.cs index 53a9972..c551d92 100644 --- a/DataObjects/CompareStatesConfiguration/CompareOutput.cs +++ b/DataObjects/CompareStatesConfiguration/CompareOutput.cs @@ -9,5 +9,8 @@ public class CompareOutput public bool Flowmaps { get; set; } public bool FlameGraphs { get; set; } public bool Configuration { get; set; } + public bool UsersGroupsRolesPermissions { get; set; } + public bool Dashboards { get; set; } + public bool Licenses { get; set; } } } diff --git a/DataObjects/CompareStatesConfiguration/CompareStatus.cs b/DataObjects/CompareStatesConfiguration/CompareStatus.cs index ca78f9b..113b344 100644 --- a/DataObjects/CompareStatesConfiguration/CompareStatus.cs +++ b/DataObjects/CompareStatesConfiguration/CompareStatus.cs @@ -5,45 +5,53 @@ public enum CompareStatus // Compare Steps CompareAPMConfiguration = 1, //CompareDBConfiguration = 2, - //CompareWEBConfiguration = 3, - //CompareMOBILEConfiguration = 4, + CompareWEBConfiguration = 3, + CompareMOBILEConfiguration = 4, //CompareBIQConfiguration = 5, - CompareAPMEntities = 10, + CompareUserGroupsRolesPermissions = 10, + CompareDashboards = 11, + CompareLicenses = 12, + + CompareAPMEntities = 20, //CompareSIMEntities = 21, //CompareDBEntities = 22, - //CompareWEBEntities = 23, - //CompareMOBILEEntities = 24, + CompareWEBEntities = 23, + CompareMOBILEEntities = 24, //CompareBIQEntities = 25, - CompareAPMMetrics = 20, + CompareAPMMetrics = 30, //CompareSIMMetrics = 21, //CompareDBMetrics = 22, //CompareWEBMetrics = 23, //CompareMOBILEMetrics = 24, //CompareBIQMetrics = 25, - CompareAPMFlowmaps = 30, + CompareAPMFlowmaps = 40, - CompareAPMSnapshots = 40, + CompareAPMSnapshots = 50, // Report steps ReportConfigurationDifferences = 100, - ReportAPMEntitiesDifferences = 110, + ReportUserGroupsRolesPermissionsDifferences = 110, + ReportDashboardsDifferences = 111, + ReportLicensesDifferences = 112, + + ReportAPMEntitiesDifferences = 120, //ReportSIMEntities = 111, //ReportDBEntities = 112, - //ReportWEBEntities = 113, - //ReportMOBILEEntities = 114, + ReportWEBEntitiesDifferences = 123, + ReportMOBILEEntities = 124, //ReportBIQEntities = 115, - ReportAPMMetricsDifferences = 120, - ReportAPMMetricGraphsDifferences = 121, + ReportAPMMetricsDifferences = 130, + ReportAPMMetricGraphsDifferences = 131, - ReportAPMFlowmapsDifferences = 122, + ReportAPMFlowmapsDifferences = 133, - ReportAPMSnapshotsDifferences = 130, - ReportAPMFlameGraphsDifferences = 132, + ReportAPMSnapshotsDifferences = 140, + ReportAPMFlameGraphsDifferences = 141, // The rest Done = 500, diff --git a/DataObjects/CompareStatesConfiguration/CompareTimeRange.cs b/DataObjects/CompareStatesConfiguration/CompareTimeRange.cs new file mode 100644 index 0000000..3b3f51c --- /dev/null +++ b/DataObjects/CompareStatesConfiguration/CompareTimeRange.cs @@ -0,0 +1,12 @@ +using System; + +namespace AppDynamics.Dexter +{ + public class CompareTimeRange + { + public int ReferenceSkip { get; set; } + public int ReferenceCompareRanges { get; set; } + public int DifferenceSkipRanges { get; set; } + public int DifferenceConsiderRanges { get; set; } + } +} diff --git a/DataObjects/JobConfiguration/JobBackendType.cs b/DataObjects/JobConfiguration/JobBackendType.cs deleted file mode 100644 index 54f0e12..0000000 --- a/DataObjects/JobConfiguration/JobBackendType.cs +++ /dev/null @@ -1,54 +0,0 @@ -namespace AppDynamics.Dexter -{ - /// - /// codebase\controller\controller-api\agent\src\main\java\com\singularity\ee\controller\api\dto\transactionmonitor\TransactionExitPointType.java - /// - public class JobBackendType - { - public bool All { get; set; } - - // Common Across Runtimes - public bool SOCKET { get; set; } - public bool HTTP { get; set; } - public bool CUSTOM { get; set; } - public bool CUSTOM_ASYNC { get; set; } - public bool FILE_SERVER { get; set; } - public bool MAIL_SERVER { get; set; } - public bool WEB_SERVICE { get; set; } - public bool ERP { get; set; } - public bool CACHE { get; set; } - public bool WEBSPHERE_MQ { get; set; } - public bool MAINFRAME { get; set; } - public bool TIBCO_ASYNC { get; set; } - public bool TIBCO { get; set; } - public bool ESB { get; set; } - public bool SAP { get; set; } - public bool AVRO { get; set; } - public bool THRIFT { get; set; } - public bool CASSANDRA { get; set; } - public bool MQ { get; set; } - public bool JMS { get; set; } - public bool WEBSOCKET { get; set; } - - // Java Only - public bool JDBC { get; set; } - public bool RMI { get; set; } - public bool LDAP { get; set; } - public bool CORBA { get; set; } - public bool RABBITMQ { get; set; } - - // .NET Only - public bool ADODOTNET { get; set; } - public bool DOTNETDirectoryServices { get; set; } - public bool DOTNETRemoting { get; set; } - public bool DOTNETMessaging { get; set; } - public bool WCF { get; set; } - public bool MSMQ { get; set; } - - // PHP specific - public bool DB { get; set; } - - // network stats - public bool NETWORK { get; set; } - } -} \ No newline at end of file diff --git a/DataObjects/JobConfiguration/JobBusinessTransactionType.cs b/DataObjects/JobConfiguration/JobBusinessTransactionType.cs deleted file mode 100644 index b1b1a7f..0000000 --- a/DataObjects/JobConfiguration/JobBusinessTransactionType.cs +++ /dev/null @@ -1,53 +0,0 @@ -namespace AppDynamics.Dexter -{ - /// - /// codebase/controller/controller-api/agent/src/main/java/com/singularity/ee/controller/api/dto/transactionmonitor/BusinessTransactionType.java - /// - public class JobBusinessTransactionType - { - public bool All { get; set; } - - // Java - public bool SERVLET { get; set; } - public bool HTTP { get; set; } - public bool WEB_SERVICE { get; set; } - public bool POJO { get; set; } - public bool JMS { get; set; } - public bool EJB { get; set; } - public bool SPRING_BEAN { get; set; } - public bool STRUTS_ACTION { get; set; } - - //.net - public bool ASP_DOTNET { get; set; } - public bool ASP_DOTNET_WEB_SERVICE { get; set; } - public bool DOTNET_REMOTING { get; set; } - public bool WCF { get; set; } - public bool DOTNET_JMS { get; set; } - public bool POCO { get; set; } - - //common - public bool BINARY_REMOTING { get; set; } - - //php - public bool PHP_WEB { get; set; } - public bool PHP_MVC { get; set; } - public bool PHP_DRUPAL { get; set; } - public bool PHP_WORDPRESS { get; set; } - public bool PHP_CLI { get; set; } - public bool PHP_WEB_SERVICE { get; set; } - - //node.js - public bool NODEJS_WEB { get; set; } - - //apache - public bool NATIVE { get; set; } - public bool WEB { get; set; } - - //Python - public bool PYTHON_WEB { get; set; } - - // Ruby - public bool RUBY_WEB { get; set; } - public bool RUBY_RAILS { get; set; } - } -} diff --git a/DataObjects/JobConfiguration/JobEntityDashboardSelectionCriteria.cs b/DataObjects/JobConfiguration/JobEntityDashboardSelectionCriteria.cs index 4352a58..603310c 100644 --- a/DataObjects/JobConfiguration/JobEntityDashboardSelectionCriteria.cs +++ b/DataObjects/JobConfiguration/JobEntityDashboardSelectionCriteria.cs @@ -3,12 +3,12 @@ public class JobEntityDashboardSelectionCriteria { public string[] Tiers { get; set; } - public JobTierType TierType { get; set; } + public string[] TierTypes { get; set; } public string[] Nodes { get; set; } - public JobTierType NodeType { get; set; } + public string[] NodeTypes { get; set; } public string[] BusinessTransactions { get; set; } - public JobBusinessTransactionType BusinessTransactionType { get; set; } + public string[] BusinessTransactionTypes { get; set; } public string[] Backends { get; set; } - public JobBackendType BackendType { get; set; } + public string[] BackendTypes { get; set; } } } diff --git a/DataObjects/JobConfiguration/JobInput.cs b/DataObjects/JobConfiguration/JobInput.cs index b77df0a..0318cc8 100644 --- a/DataObjects/JobConfiguration/JobInput.cs +++ b/DataObjects/JobConfiguration/JobInput.cs @@ -4,6 +4,7 @@ namespace AppDynamics.Dexter { public class JobInput { + public TimeFrame TimeFrame { get; set; } public JobTimeRange TimeRange { get; set; } public List HourlyTimeRanges { get; set; } public JobSnapshotSelectionCriteria SnapshotSelectionCriteria { get; set; } diff --git a/DataObjects/JobConfiguration/JobOutput.cs b/DataObjects/JobConfiguration/JobOutput.cs index da840f3..212a783 100644 --- a/DataObjects/JobConfiguration/JobOutput.cs +++ b/DataObjects/JobConfiguration/JobOutput.cs @@ -8,6 +8,7 @@ public class JobOutput public bool EntityDetails { get; set; } public bool EntityDashboards { get; set; } public bool Snapshots { get; set; } + public bool Flowmaps { get; set; } public bool FlameGraphs { get; set; } public bool Configuration { get; set; } public bool Events { get; set; } diff --git a/DataObjects/JobConfiguration/JobSnapshotSelectionCriteria.cs b/DataObjects/JobConfiguration/JobSnapshotSelectionCriteria.cs index 165c48d..6de8472 100644 --- a/DataObjects/JobConfiguration/JobSnapshotSelectionCriteria.cs +++ b/DataObjects/JobConfiguration/JobSnapshotSelectionCriteria.cs @@ -3,9 +3,9 @@ public class JobSnapshotSelectionCriteria { public string[] Tiers { get; set; } - public JobTierType TierType { get; set; } + public string[] TierTypes { get; set; } public string[] BusinessTransactions { get; set; } - public JobBusinessTransactionType BusinessTransactionType { get; set; } + public string[] BusinessTransactionTypes { get; set; } public JobUserExperience UserExperience { get; set; } public JobSnapshotType SnapshotType { get; set; } } diff --git a/DataObjects/JobConfiguration/JobStatus.cs b/DataObjects/JobConfiguration/JobStatus.cs index 85922f8..a606512 100644 --- a/DataObjects/JobConfiguration/JobStatus.cs +++ b/DataObjects/JobConfiguration/JobStatus.cs @@ -83,6 +83,7 @@ public enum JobStatus ReportAPMMetrics = 120, ReportAPMMetricGraphs = 121, + ReportAPMFlowmaps = 122, ReportAPMSnapshots = 130, ReportAPMSnapshotsMethodCallLines = 131, diff --git a/DataObjects/JobConfiguration/JobTierType.cs b/DataObjects/JobConfiguration/JobTierType.cs deleted file mode 100644 index d810a9f..0000000 --- a/DataObjects/JobConfiguration/JobTierType.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace AppDynamics.Dexter -{ - public class JobTierType - { - public bool All { get; set; } - public bool APP_AGENT { get; set; } - public bool DOT_NET_APP_AGENT { get; set; } - public bool NATIVE_APP_AGENT { get; set; } - public bool NATIVE_DYNAMIC { get; set; } - public bool NATIVE_SDK { get; set; } - public bool NATIVE_WEB_SERVER { get; set; } - public bool NODEJS_APP_AGENT { get; set; } - public bool PHP_APP_AGENT { get; set; } - public bool PYTHON_APP_AGENT { get; set; } - public bool RUBY_APP_AGENT { get; set; } - } -} diff --git a/DataObjects/JobConfiguration/JobTimeFrame.cs b/DataObjects/JobConfiguration/JobTimeFrame.cs new file mode 100644 index 0000000..e649477 --- /dev/null +++ b/DataObjects/JobConfiguration/JobTimeFrame.cs @@ -0,0 +1,20 @@ +using System; + +namespace AppDynamics.Dexter +{ + public class TimeFrame + { + public string MarkDate { get; set; } + public string MarkTime { get; set; } + public string Duration { get; set; } + + public override string ToString() + { + return String.Format( + "TimeFrame: {0} {1} {2}", + this.MarkDate, + this.MarkTime, + this.Duration); + } + } +} diff --git a/DataObjects/JobConfiguration/JobTimeRange.cs b/DataObjects/JobConfiguration/JobTimeRange.cs index c3ca48f..f3e20c2 100644 --- a/DataObjects/JobConfiguration/JobTimeRange.cs +++ b/DataObjects/JobConfiguration/JobTimeRange.cs @@ -6,7 +6,7 @@ public class JobTimeRange { public DateTime From { get; set; } public DateTime To { get; set; } - public override String ToString() + public override string ToString() { return String.Format( "JobTimeRange: {0:o}-{1:o} {2:o}-{3:o}", diff --git a/DataObjects/ProgramOptions/ProgramOptions.cs b/DataObjects/ProgramOptions/ProgramOptions.cs index 93df8c7..3559809 100644 --- a/DataObjects/ProgramOptions/ProgramOptions.cs +++ b/DataObjects/ProgramOptions/ProgramOptions.cs @@ -5,30 +5,35 @@ namespace AppDynamics.Dexter { public class ProgramOptions { + // ETL [Option('j', "job-file", Required = true, SetName = "etl", HelpText = "Input file defining the parameters of the ETL job to process.")] - public string InputJobFilePath { get; set; } + public string InputETLJobFilePath { get; set; } - [Option('o', "output-folder", Required = false, HelpText = "Output folder where to store results of processing.")] - public string OutputFolderPath { get; set; } + // Compare States + //[Option('c', "compare-states-file", Required = true, SetName = "compare", HelpText = "Compare file defining the mappings of the state comparison to perform.")] + public string InputCompareJobFilePath { get; set; } - [Option('c', "compare-states-file", Required = true, SetName = "compare", HelpText = "Compare file defining the mappings of the state comparison to perform.")] - public string CompareFilePath { get; set; } + //[Option('l', "left-from-state-folder", Required = true, SetName = "compare", HelpText = "Folder of the ETL job to use as a left side/reference/from comparison.")] + public string CompareReportReferenceFolderPath { get; set; } - [Option('l', "left-from-state-folder", Required = true, SetName = "compare", HelpText = "Folder of the ETL job to use as a left side/reference/from comparison.")] - public string ReferenceJobFolderPath { get; set; } + //[Option('r', "right-to-state-folder", Required = true, SetName = "compare", HelpText = "Folder of the ETL job to use as a right side/difference/to comparison.")] + public string CompareReportDifferenceFolderPath { get; set; } - [Option('r', "right-to-state-folder", Required = true, SetName = "compare", HelpText = "Folder of the ETL job to use as a right side/difference/to comparison.")] - public string DifferenceJobFolderPath { get; set; } + // Common + [Option('o', "output-folder", Required = false, HelpText = "Output folder where to store results of processing.")] + public string OutputFolderPath { get; set; } [Option('d', "delete-previous-job-output", Required = false, HelpText = "If true, delete any results of previous processing.")] - public bool RestartJobFromBeginning { get; set; } + public bool DeletePreviousJobOutput { get; set; } [Option('s', "sequential-processing", Required = false, HelpText = "If true, process certain items during extraction and conversion sequentially.")] public bool ProcessSequentially { get; set; } + // Version check [Option('v', "skip-version-check", Required = false, HelpText = "If true, skips the version check against GitHub repository.")] public bool SkipVersionCheck { get; set; } + public string OutputJobFolderPath { get; set; } public string OutputJobFilePath { get; set; } @@ -53,17 +58,19 @@ public override string ToString() CompareFilePath='{7}' ReferenceJobFolderPath='{8}' DifferenceJobFolderPath='{9}' +SkipVersionCheck='{10}' ", - this.InputJobFilePath, - this.RestartJobFromBeginning, + this.InputETLJobFilePath, + this.DeletePreviousJobOutput, this.OutputFolderPath, this.OutputJobFolderPath, this.OutputJobFilePath, this.ProcessSequentially, this.ProgramLocationFolderPath, - this.CompareFilePath, - this.ReferenceJobFolderPath, - this.DifferenceJobFolderPath); + this.InputCompareJobFilePath, + this.CompareReportReferenceFolderPath, + this.CompareReportDifferenceFolderPath, + this.SkipVersionCheck); } } } diff --git a/CompareStates.json b/DefaultCompare.json similarity index 67% rename from CompareStates.json rename to DefaultCompare.json index 6028a97..db4fec1 100644 --- a/CompareStates.json +++ b/DefaultCompare.json @@ -13,6 +13,8 @@ "ReferenceApplication": "SpecificApplication", "DifferenceController": "https://your.controller.here", "DifferenceApplication": "AnotherSpecificApplication", + "NumTimeRangesOffset": 0, + "NumTimeRangesCompare": -1, "NameRegex": false, "Type": "APM" }, @@ -26,6 +28,19 @@ } ], "Input": { + "TimeRange": { + "ReferenceSkipRanges": 0, + "ReferenceCompareRanges": -1, + "DifferenceSkipRanges": 0, + "DifferenceConsiderRanges": -1, + "xReferenceFrom": "2020-03-20T09:00:00", + "xReferenceTo": "2020-03-20T10:00:00", + "xDifferenceFrom": "2020-03-20T09:00:00", + "xDifferenceTo": "2020-03-20T10:00:00" + }, + "UsersGroupsRolesPermissions": true, + "Dashboards": true, + "Licenses": true, "Configuration": true, "DetectedEntities": true, "Metrics": true, @@ -33,6 +48,9 @@ "Snapshots": true }, "Output": { + "UsersGroupsRolesPermissions": true, + "Dashboards": true, + "Licenses": true, "Configuration": true, "DetectedEntities": true, "EntityMetrics": true, diff --git a/DefaultJob.json b/DefaultJob.json index 32f3648..ac1eab0 100644 --- a/DefaultJob.json +++ b/DefaultJob.json @@ -1,4 +1,5 @@ { + "__DOCUMENTATION__": "Please read https://github.com/Appdynamics/AppDynamics.DEXTER/wiki/Job-File for information on how to configure parameters in this file", "Target": [ { "Controller": "https://your.controller.here", @@ -50,19 +51,37 @@ } ], "Input": { - "TimeRange": { - "From": "2020-03-20T09:00:00", - "To": "2020-03-20T10:00:00" + "TimeFrame": { + "__DOCUMENTATION__": "Please read https://github.com/Appdynamics/AppDynamics.DEXTER/wiki/Job-File#timeframe for information on how to configure TimeFrame parameter", + "__DOCUMENTATION_MarkDate__": "Possible values: [2020-05-05 (YYY-MM-DD) | 2020-05-05Z | TODAY | TODAY_Z | YESTERDAY | YESTERDAY_Z | DAY_BEFORE_YESTERDAY | DAY_BEFORE_YESTERDAY_Z | SAME_DAY_LAST_WEEK | SAME_DAY_LAST_WEEK_Z | MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY | MONDAY_Z, TUESDAY_Z, WEDNESDAY_Z, THURSDAY_Z, FRIDAY_Z, SATURDAY_Z, SUNDAY_Z | DAY_OF_MONTH_# | DAY_OF_MONTH_#_Z]", + "__DOCUMENTATION_MarkTime__": "Possible values: [14:00:00 (HH:mm:ss) local time | 14:00:00Z GMT | 07:15:00-04:00 Specific Time Zone | NOW | NOW_Z | CURRENT_HOUR | CURRENT_HOUR_Z | PREVIOUS_HOUR | PREVIOUS_HOUR_Z ]", + "__DOCUMENTATION_Duration__": "Possible values: [1:00:00 ([-][d.]hh:mm:ss) | PT1H (P[n]Y[n]M[n]DT[n]H[n]M[n]S) ]", + "MarkDate": "TODAY", + "MarkTime": "PREVIOUS_HOUR", + "Duration": "1:00:00" }, "UsersGroupsRolesPermissions": true, "Dashboards": true, + "Licenses": false, "Events": true, "EventsSelectionCriteria": [ "All" ], "Configuration": true, - "ConfigurationComparisonReferenceAPM": { "Controller": "", "Application": "" }, - "ConfigurationComparisonReferenceWEB": { "Controller": "", "Application": "" }, - "ConfigurationComparisonReferenceMOBILE": { "Controller": "", "Application": "" }, - "ConfigurationComparisonReferenceDB": { "Controller": "", "Application": "" }, + "ConfigurationComparisonReferenceAPM": { + "Controller": "", + "Application": "" + }, + "ConfigurationComparisonReferenceWEB": { + "Controller": "", + "Application": "" + }, + "ConfigurationComparisonReferenceMOBILE": { + "Controller": "", + "Application": "" + }, + "ConfigurationComparisonReferenceDB": { + "Controller": "", + "Application": "" + }, "DetectedEntities": true, "Metrics": true, "MetricsSelectionCriteria": [ "TransactionApplication", "TransactionTier", "TransactionNode", "TransactionBackend", "TransactionBT", "TransactionSEP", "TransactionError", "TransactionIP", "AgentStats", "OSHardware", "CLRStats", "IISStats", "IISReqCounts", "IISReqPerf", "JVMStats", "BusinessTransactionPercentiles" ], @@ -70,50 +89,20 @@ "EntityDashboards": false, "EntityDashboardSelectionCriteria": { "Tiers": [], - "TierType": { - "All": true, - "APP_AGENT": false, "DOT_NET_APP_AGENT": false, "NATIVE_APP_AGENT": false, "NATIVE_DYNAMIC": false, "NATIVE_SDK": false, - "NATIVE_WEB_SERVER": false, "NODEJS_APP_AGENT": false, "PHP_APP_AGENT": false, "PYTHON_APP_AGENT": false, "RUBY_APP_AGENT": false - }, + "TierTypes": [ "All" ], "BusinessTransactions": [], - "BusinessTransactionType": { - "All": true, - "SERVLET": false, "HTTP": false, "WEB_SERVICE": false, "POJO": false, "JMS": false, "EJB": false, "SPRING_BEAN": false, "STRUTS_ACTION": false, - "ASP_DOTNET": false, "ASP_DOTNET_WEB_SERVICE": false, "DOTNET_REMOTING": false, "WCF": false, - "DOTNET_JMS": false, "POCO": false, "PHP_WEB": false, "PHP_MVC": false, "PHP_DRUPAL": false,"PHP_WORDPRESS": false, - "PHP_CLI": false, "PHP_WEB_SERVICE": false, "NODEJS_WEB": false, "NATIVE": false, "WEB": false, "PYTHON_WEB": false, "RUBY_WEB": false, "RUBY_RAILS": false, "BINARY_REMOTING": false - }, + "BusinessTransactionTypes": [ "All" ], "Nodes": [], - "NodeType": { - "All": true, - "APP_AGENT": false, "DOT_NET_APP_AGENT": false, "NATIVE_APP_AGENT": false, "NATIVE_DYNAMIC": false, "NATIVE_SDK": false, - "NATIVE_WEB_SERVER": false, "NODEJS_APP_AGENT": false, "PHP_APP_AGENT": false, "PYTHON_APP_AGENT": false, "RUBY_APP_AGENT": false - }, + "NodeTypes": [ "All" ], "Backends": [], - "BackendType": { - "All": true, - "SOCKET": false, "HTTP": false, "CUSTOM": false, "CUSTOM_ASYNC": false, "FILE_SERVER": false, "MAIL_SERVER": false, "WEB_SERVICE": false, "ERP": false, - "CACHE": false, "WEBSPHERE_MQ": false, "MAINFRAME": false, "TIBCO_ASYNC": false, "TIBCO": false, "ESB": false, "SAP": false, "AVRO": false, - "THRIFT": false, "CASSANDRA": false, "MQ": false, "JMS": false, "WEBSOCKET": false, "JDBC": false, "RMI": false, "LDAP": false, "CORBA": false, - "RABBITMQ": false, "ADODOTNET": false, "DOTNETDirectoryServices": false, "DOTNETRemoting": false, "DOTNETMessaging": false, "WCF": false, "MSMQ": false, "DB": false, "NETWORK": false - } + "BackendTypes": [ "All" ] }, "Snapshots": true, "SnapshotSelectionCriteria": { "Tiers": [], - "TierType": { - "All": true, - "APP_AGENT": false, "DOT_NET_APP_AGENT": false, "NATIVE_APP_AGENT": false, "NATIVE_DYNAMIC": false, "NATIVE_SDK": false, - "NATIVE_WEB_SERVER": false, "NODEJS_APP_AGENT": false, "PHP_APP_AGENT": false, "PYTHON_APP_AGENT": false, "RUBY_APP_AGENT": false - }, + "TierTypes": [ "All" ], "BusinessTransactions": [], - "BusinessTransactionType": { - "All": true, - "SERVLET": false, "HTTP": false, "WEB_SERVICE": false, "POJO": false, "JMS": false, "EJB": false, "SPRING_BEAN": false, "STRUTS_ACTION": false, - "ASP_DOTNET": false, "ASP_DOTNET_WEB_SERVICE": false, "DOTNET_REMOTING": false, "WCF": false, - "DOTNET_JMS": false, "POCO": false, "PHP_WEB": false, "PHP_MVC": false, "PHP_DRUPAL": false,"PHP_WORDPRESS": false, - "PHP_CLI": false, "PHP_WEB_SERVICE": false, "NODEJS_WEB": false, "NATIVE": false, "WEB": false, "PYTHON_WEB": false, "RUBY_WEB": false, "RUBY_RAILS": false, "BINARY_REMOTING": false - }, + "BusinessTransactionTypes": [ "All" ], "UserExperience": { "Normal": true, "Slow": true, @@ -131,11 +120,13 @@ "Output": { "UsersGroupsRolesPermissions": true, "Dashboards": true, + "Licenses": false, "Events": true, "Configuration": true, "DetectedEntities": true, "EntityMetrics": true, "EntityMetricGraphs": true, + "Flowmaps": true, "EntityDashboards": true, "EntityDetails": true, "Snapshots": true, diff --git a/EntityMetricsExtractMapping.csv b/EntityMetricsExtractMapping.csv index fcb1d8c..766c3b2 100644 --- a/EntityMetricsExtractMapping.csv +++ b/EntityMetricsExtractMapping.csv @@ -13,8 +13,9 @@ Tier,Overall Application Performance|*|External Calls|*|Average Response Time (m Tier,Overall Application Performance|*|External Calls|*|Calls per Minute,Calls per Minute,BACKCPM,SUM,,PRIMARY,0000FF,BackendCallsPerTier Tier,Overall Application Performance|*|External Calls|*|Errors per Minute,Errors per Minute,BACKEPM,SUM,,PRIMARY,FF0000,BackendCallsPerTier Tier,Overall Application Performance|*|External Calls|Call-JDBC to Discovered backend call - XE-Oracle DB-oracle-db-Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production|Calls per Minute,Call-JDBC to Discovered backend call - XE-Oracle DB-oracle-db-Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production|Calls per Minute,BACKCPMSPEC,SUM,SpecSQLCPM,PRIMARY,0000FF,BackendCallsSpecificCustom1 -Tier,Application Infrastructure Performance|*|Agent|App|Availability,Agent|App|Availability,AVAILAGENT,SUM,AgentAvailability,PRIMARY,108000,AgentStats -Tier,Application Infrastructure Performance|*|Agent|Machine|Availability,Agent|Machine|Availability,AVAILMACHINE,SUM,AgentAvailability,PRIMARY,0000FF,AgentStats +Tier,Application Infrastructure Performance|*|Agent|App|Availability,Agent|App|Availability,AVAILAGENT,SUM,AgentStatistics,PRIMARY,108000,AgentStats +Tier,Application Infrastructure Performance|*|Agent|Machine|Availability,Agent|Machine|Availability,AVAILMACHINE,SUM,AgentStatistics,PRIMARY,0000FF,AgentStats +Tier,Application Infrastructure Performance|*|Agent|Metric Upload|Metrics uploaded,Agent|Metric Upload|Metrics uploaded,METRUPL,SUM,AgentStatistics,SECONDARY,FFA500,AgentStats Tier,Application Infrastructure Performance|*|Hardware Resources|CPU|%Busy,Hardware Resources|CPU|%Busy,CPUBUSY,AVERAGE,OSCPUMemory,PRIMARY,108000,OSHardware Tier,Application Infrastructure Performance|*|Hardware Resources|Memory|Used (MB),Hardware Resources|Memory|Used (MB),MEMUSEDMB,AVERAGE,OSCPUMemory,SECONDARY,0000FF,OSHardware Tier,Application Infrastructure Performance|*|Hardware Resources|Disks|KB written/sec,Hardware Resources|Disks|KB written/sec,DISKKBWRITE,AVERAGE,OSDisk,PRIMARY,108000,OSHardware @@ -70,8 +71,9 @@ Node,Overall Application Performance|*|Individual Nodes|*|External Calls|*|Avera Node,Overall Application Performance|*|Individual Nodes|*|External Calls|*|Calls per Minute,Calls per Minute,BACKCPM,SUM,,PRIMARY,0000FF,BackendCallsPerNode Node,Overall Application Performance|*|Individual Nodes|*|External Calls|*|Errors per Minute,Errors per Minute,BACKEPM,SUM,,PRIMARY,FF0000,BackendCallsPerNode Node,Overall Application Performance|*|Individual Nodes|*|External Calls|Call-JDBC to Discovered backend call - XE-Oracle DB-oracle-db-Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production|Calls per Minute,Call-JDBC to Discovered backend call - XE-Oracle DB-oracle-db-Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production|Calls per Minute,BACKCPMSPEC,SUM,SpecSQLCPM,PRIMARY,0000FF,BackendCallsSpecificCustom1 -Node,Application Infrastructure Performance|*|Individual Nodes|*|Agent|App|Availability,Agent|App|Availability,AVAILAGENT,SUM,AgentAvailability,PRIMARY,108000,AgentStats -Node,Application Infrastructure Performance|*|Individual Nodes|*|Agent|Machine|Availability,Agent|Machine|Availability,AVAILMACHINE,SUM,AgentAvailability,PRIMARY,0000FF,AgentStats +Node,Application Infrastructure Performance|*|Individual Nodes|*|Agent|App|Availability,Agent|App|Availability,AVAILAGENT,SUM,AgentStatistics,PRIMARY,108000,AgentStats +Node,Application Infrastructure Performance|*|Individual Nodes|*|Agent|Machine|Availability,Agent|Machine|Availability,AVAILMACHINE,SUM,AgentStatistics,PRIMARY,0000FF,AgentStats +Node,Application Infrastructure Performance|*|Individual Nodes|*|Agent|Metric Upload|Metrics uploaded,Agent|Metric Upload|Metrics uploaded,METRUPL,SUM,AgentStatistics,SECONDARY,FFA500,AgentStats Node,Application Infrastructure Performance|*|Individual Nodes|*|Hardware Resources|CPU|%Busy,Hardware Resources|CPU|%Busy,CPUBUSY,AVERAGE,OSCPUMemory,PRIMARY,108000,OSHardware Node,Application Infrastructure Performance|*|Individual Nodes|*|Hardware Resources|Memory|Used (MB),Hardware Resources|Memory|Used (MB),MEMUSEDMB,AVERAGE,OSCPUMemory,SECONDARY,0000FF,OSHardware Node,Application Infrastructure Performance|*|Individual Nodes|*|Hardware Resources|Disks|KB written/sec,Hardware Resources|Disks|KB written/sec,DISKKBWRITE,AVERAGE,OSDisk,PRIMARY,108000,OSHardware diff --git a/Helpers/FileIOHelper.cs b/Helpers/FileIOHelper.cs index 02fd93f..b686ea0 100644 --- a/Helpers/FileIOHelper.cs +++ b/Helpers/FileIOHelper.cs @@ -382,7 +382,7 @@ public static JobConfiguration ReadJobConfigurationFromFile(string configuration } catch (Exception ex) { - loggerConsole.Error(ex); + loggerConsole.Error(ex.Message); logger.Error("Unable to load JobConfiguration JSON from job file {0}", configurationFilePath); logger.Error(ex); } diff --git a/JobEditor/HTMLPage1.html b/JobEditor/HTMLPage1.html new file mode 100644 index 0000000..8a3ab90 --- /dev/null +++ b/JobEditor/HTMLPage1.html @@ -0,0 +1,9 @@ + + + + TypeScript Greeter + + + + + \ No newline at end of file diff --git a/JobEditor/JobEditor.html b/JobEditor/JobEditor.html index 8f15f3e..382650c 100644 --- a/JobEditor/JobEditor.html +++ b/JobEditor/JobEditor.html @@ -10,5 +10,259 @@ Blah blah blah +
+ + \ No newline at end of file diff --git a/JobEditor/JobEditor.js b/JobEditor/JobEditor.js index d315143..3e7060b 100644 --- a/JobEditor/JobEditor.js +++ b/JobEditor/JobEditor.js @@ -2,4 +2,23 @@ function helloWorld() { window.alert("hi!"); + + doDownload(document.getElementById("textAreaJobFile").innerHTML); +} + +function dataUrl(data) +{ + return "data:x-application/xml;charset=utf-8," + escape(data); +} + + +function doDownload(str) +{ + var downloadLink = document.createElement("a"); + downloadLink.href = dataUrl(str); + downloadLink.download = "test.json"; + + document.body.appendChild(downloadLink); + downloadLink.click(); + document.body.removeChild(downloadLink); } \ No newline at end of file diff --git a/JobEditor/file1.js b/JobEditor/file1.js new file mode 100644 index 0000000..c09e5b9 --- /dev/null +++ b/JobEditor/file1.js @@ -0,0 +1,15 @@ +var Student = /** @class */ (function () { + function Student(firstName, middleInitial, lastName) { + this.firstName = firstName; + this.middleInitial = middleInitial; + this.lastName = lastName; + this.fullName = firstName + " " + middleInitial + " " + lastName; + } + return Student; +}()); +function greeter(person) { + return "Hello, " + person.firstName + " " + person.lastName; +} +var user = new Student("Jane", "M.", "User"); +document.body.textContent = greeter(user); +//# sourceMappingURL=file1.js.map \ No newline at end of file diff --git a/JobEditor/file1.js.map b/JobEditor/file1.js.map new file mode 100644 index 0000000..eaf5731 --- /dev/null +++ b/JobEditor/file1.js.map @@ -0,0 +1 @@ +{"version":3,"file":"file1.js","sourceRoot":"","sources":["file1.ts"],"names":[],"mappings":"AAAA;IAEI,iBAAmB,SAAiB,EAAS,aAAqB,EAAS,QAAgB;QAAxE,cAAS,GAAT,SAAS,CAAQ;QAAS,kBAAa,GAAb,aAAa,CAAQ;QAAS,aAAQ,GAAR,QAAQ,CAAQ;QACvF,IAAI,CAAC,QAAQ,GAAG,SAAS,GAAG,GAAG,GAAG,aAAa,GAAG,GAAG,GAAG,QAAQ,CAAC;IACrE,CAAC;IACL,cAAC;AAAD,CAAC,AALD,IAKC;AAOD,SAAS,OAAO,CAAC,MAAc;IAC3B,OAAO,SAAS,GAAG,MAAM,CAAC,SAAS,GAAG,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC;AAChE,CAAC;AAED,IAAI,IAAI,GAAG,IAAI,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AAE7C,QAAQ,CAAC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC"} \ No newline at end of file diff --git a/JobEditor/file1.ts b/JobEditor/file1.ts new file mode 100644 index 0000000..1144822 --- /dev/null +++ b/JobEditor/file1.ts @@ -0,0 +1,19 @@ +class Student { + fullName: string; + constructor(public firstName: string, public middleInitial: string, public lastName: string) { + this.fullName = firstName + " " + middleInitial + " " + lastName; + } +} + +interface Person { + firstName: string; + lastName: string; +} + +function greeter(person: Person) { + return "Hello, " + person.firstName + " " + person.lastName; +} + +let user = new Student("Jane", "M.", "User"); + +document.body.textContent = greeter(user); \ No newline at end of file diff --git a/LicenseSign/IssueCertPS/ExportCert.ps1 b/LicenseSign/IssueCertPS/ExportCert.ps1 new file mode 100644 index 0000000..e277a0f --- /dev/null +++ b/LicenseSign/IssueCertPS/ExportCert.ps1 @@ -0,0 +1,11 @@ +#https://docs.microsoft.com/en-us/powershell/module/pkiclient/new-selfsignedcertificate?view=win10-ps + +$cert = Get-ChildItem -Path "cert:\CurrentUser\My" | where {$_.Subject -eq "CN=AppDynamics DEXTER"} +$cert + +Export-Certificate -Cert $cert -FilePath "AppDynamics.DEXTER.cer" + +$credential = Get-Credential +$password = $credential.Password + +Export-PFXCertificate -Cert $cert -FilePath "AppDynamics.DEXTER.pfx" -Password $password diff --git a/LicenseSign/IssueCertPS/ImportCert.ps1 b/LicenseSign/IssueCertPS/ImportCert.ps1 new file mode 100644 index 0000000..835d773 --- /dev/null +++ b/LicenseSign/IssueCertPS/ImportCert.ps1 @@ -0,0 +1,6 @@ +#https://docs.microsoft.com/en-us/powershell/module/pkiclient/new-selfsignedcertificate?view=win10-ps + +$credential = Get-Credential +$password = $credential.Password + +Import-PfxCertificate -FilePath "AppDynamics.DEXTER.pfx" -CertStoreLocation "cert:\CurrentUser\My" -Exportable -Password $password diff --git a/LicenseSign/IssueCertPS/IssueCert.ps1 b/LicenseSign/IssueCertPS/IssueCert.ps1 new file mode 100644 index 0000000..550e5eb --- /dev/null +++ b/LicenseSign/IssueCertPS/IssueCert.ps1 @@ -0,0 +1,5 @@ +#https://docs.microsoft.com/en-us/powershell/module/pkiclient/new-selfsignedcertificate?view=win10-ps + +$dateOfExpiration = New-Object DateTime 2030, 12, 31, 23, 59, 59, ([DateTimeKind]::Utc) + +New-SelfSignedCertificate -Subject "AppDynamics DEXTER" -KeyFriendlyName "AppDynamics DEXTER" -CertStoreLocation "cert:\CurrentUser\My" -KeyExportPolicy Exportable -NotAfter $dateOfExpiration \ No newline at end of file diff --git a/LicenseSign/IssueCertificate/App.config b/LicenseSign/IssueCertificate/App.config new file mode 100644 index 0000000..56efbc7 --- /dev/null +++ b/LicenseSign/IssueCertificate/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/LicenseSign/IssueCertificate/IssueCertificate.csproj b/LicenseSign/IssueCertificate/IssueCertificate.csproj new file mode 100644 index 0000000..6b79742 --- /dev/null +++ b/LicenseSign/IssueCertificate/IssueCertificate.csproj @@ -0,0 +1,53 @@ + + + + + Debug + AnyCPU + {5EC40CAF-7CA0-4C22-8F3D-78BC647FB5FB} + Exe + IssueCertificate + IssueCertificate + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LicenseSign/IssueCertificate/Program.cs b/LicenseSign/IssueCertificate/Program.cs new file mode 100644 index 0000000..729ed17 --- /dev/null +++ b/LicenseSign/IssueCertificate/Program.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Security; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; + +namespace IssueCertificate +{ + class Program + { + static void Main(string[] args) + { + if (args.Length != 2) + { + Console.WriteLine("Not enough parameters passed"); + return; + } + + string certificatePrivateAndPublicFilePath = args[0]; + string certificatePublicKeyFilePath = args[1]; + + var oldCertificate = CreateCertificate(); + var oldCertificateBytes = oldCertificate.Export(X509ContentType.Pfx, ""); + var newCertificate = new X509Certificate2(oldCertificateBytes, "", + X509KeyStorageFlags.Exportable | + X509KeyStorageFlags.MachineKeySet | + X509KeyStorageFlags.PersistKeySet); + + LogCertificate(oldCertificate, "old certificate"); // this fails + LogCertificate(newCertificate, "new certificate"); // works only on Win10 + + SaveCertificatePublicAndPrivate(newCertificate, certificatePrivateAndPublicFilePath); + SaveCertificatePublicOnly(newCertificate, certificatePublicKeyFilePath); + } + + private static X509Certificate2 CreateCertificate() + { + var keyParams = new CngKeyCreationParameters(); + keyParams.KeyUsage = CngKeyUsages.Signing; + keyParams.Provider = CngProvider.MicrosoftSoftwareKeyStorageProvider; + keyParams.ExportPolicy = CngExportPolicies.AllowExport; // here I don't have AllowPlaintextExport + // keyParams.ExportPolicy = CngExportPolicies.AllowExport | CngExportPolicies.AllowPlaintextExport; // here I don't have AllowPlaintextExport + keyParams.Parameters.Add(new CngProperty("Length", BitConverter.GetBytes(2048), CngPropertyOptions.None)); + var cngKey = CngKey.Create(CngAlgorithm.Rsa, Guid.NewGuid().ToString(), keyParams); + var rsaKey = new RSACng(cngKey); + var req = new CertificateRequest("cn=AppDynamics DEXTER Licensing", rsaKey, HashAlgorithmName.SHA256, RSASignaturePadding.Pss); // requires .net 4.7.2 + var cert = req.CreateSelfSigned(DateTimeOffset.Now, new DateTime(2030, 12, 31, 23, 59, 59, DateTimeKind.Utc)); + return cert; + } + + private static void LogCertificate(X509Certificate2 certificate, string name) + { + Console.WriteLine("----- Testing " + name + " ------"); + Console.WriteLine(certificate); + + try + { + var rsaPrivateKey = certificate.GetRSAPrivateKey(); + var parameters = rsaPrivateKey.ExportParameters(true); + Console.WriteLine("Certificate private key RSA parameters were successfully exported."); + + var privateKey = certificate.PrivateKey; + Console.WriteLine("Certificate private key is accessible."); + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + } + } + + private static void SaveCertificatePublicAndPrivate(X509Certificate2 certificate, string certificateFilePath) + { + try + { + Console.WriteLine("Specify password for {0}", certificateFilePath); + String password = ReadPassword('*'); + SecureString securePassword = new NetworkCredential("", password).SecurePassword; + + byte[] certBytes = certificate.Export(X509ContentType.Pfx, securePassword); + + File.WriteAllBytes(certificateFilePath, certBytes); + + Console.WriteLine("Wrote {0} bytes to {1}", certBytes.Length, certificateFilePath); + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + } + } + + private static void SaveCertificatePublicOnly(X509Certificate2 certificate, string certificateFilePath) + { + try + { + byte[] certBytes = certificate.Export(X509ContentType.Cert); + + File.WriteAllBytes(certificateFilePath, certBytes); + + Console.WriteLine("Wrote {0} bytes to {1}", certBytes.Length, certificateFilePath); + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + } + } + + public static string ReadPassword(char mask) + { + const int ENTER = 13, BACKSP = 8, CTRLBACKSP = 127; + int[] FILTERED = { 0, 27, 9, 10 /*, 32 space, if you care */ }; // const + + var pass = new Stack(); + char chr = (char)0; + + while ((chr = System.Console.ReadKey(true).KeyChar) != ENTER) + { + if (chr == BACKSP) + { + if (pass.Count > 0) + { + System.Console.Write("\b \b"); + pass.Pop(); + } + } + else if (chr == CTRLBACKSP) + { + while (pass.Count > 0) + { + System.Console.Write("\b \b"); + pass.Pop(); + } + } + else if (FILTERED.Count(x => chr == x) > 0) { } + else + { + pass.Push((char)chr); + System.Console.Write(mask); + } + } + + return new string(pass.Reverse().ToArray()); + } + } +} \ No newline at end of file diff --git a/LicenseSign/IssueCertificate/Properties/AssemblyInfo.cs b/LicenseSign/IssueCertificate/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..57bf6e7 --- /dev/null +++ b/LicenseSign/IssueCertificate/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("IssueCertificate")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("IssueCertificate")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("5ec40caf-7ca0-4c22-8f3d-78bc647fb5fb")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/LicenseSign/LicenseSign.sln b/LicenseSign/LicenseSign.sln new file mode 100644 index 0000000..be204db --- /dev/null +++ b/LicenseSign/LicenseSign.sln @@ -0,0 +1,42 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29728.190 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IssueCertificate", "IssueCertificate\IssueCertificate.csproj", "{5EC40CAF-7CA0-4C22-8F3D-78BC647FB5FB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LicenseSign", "LicenseSign\LicenseSign.csproj", "{F4276B07-185C-46B6-BB3D-2613BFF04355}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A2D31B51-648C-461D-9C0B-3A24C0D0395F}" + ProjectSection(SolutionItems) = preProject + ..\src\LicensedFeatures.json = ..\src\LicensedFeatures.json + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LicenseValidate", "LicenseValidate\LicenseValidate.csproj", "{A9E80274-7E9A-4EB7-9360-BE54F95DD854}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5EC40CAF-7CA0-4C22-8F3D-78BC647FB5FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5EC40CAF-7CA0-4C22-8F3D-78BC647FB5FB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5EC40CAF-7CA0-4C22-8F3D-78BC647FB5FB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5EC40CAF-7CA0-4C22-8F3D-78BC647FB5FB}.Release|Any CPU.Build.0 = Release|Any CPU + {F4276B07-185C-46B6-BB3D-2613BFF04355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F4276B07-185C-46B6-BB3D-2613BFF04355}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F4276B07-185C-46B6-BB3D-2613BFF04355}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F4276B07-185C-46B6-BB3D-2613BFF04355}.Release|Any CPU.Build.0 = Release|Any CPU + {A9E80274-7E9A-4EB7-9360-BE54F95DD854}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9E80274-7E9A-4EB7-9360-BE54F95DD854}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9E80274-7E9A-4EB7-9360-BE54F95DD854}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9E80274-7E9A-4EB7-9360-BE54F95DD854}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1CE602F2-76DE-49A4-A550-BF79CA5074FB} + EndGlobalSection +EndGlobal diff --git a/LicenseSign/LicenseSign/LicenseSign.csproj b/LicenseSign/LicenseSign/LicenseSign.csproj new file mode 100644 index 0000000..f730ef5 --- /dev/null +++ b/LicenseSign/LicenseSign/LicenseSign.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp3.1 + + + + + + + diff --git a/LicenseSign/LicenseSign/Program.cs b/LicenseSign/LicenseSign/Program.cs new file mode 100644 index 0000000..185f86f --- /dev/null +++ b/LicenseSign/LicenseSign/Program.cs @@ -0,0 +1,149 @@ +using Newtonsoft.Json.Linq; +using System; +using System.IO; +using Newtonsoft.Json; +using System.Text; +using System.Security; +using System.Net; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using System.Security.Cryptography; + +namespace LicenseSign +{ + class Program + { + static void Main(string[] args) + { + if (args.Length != 2) + { + Console.WriteLine("path_to_license_file name_of_certificate"); + Console.WriteLine("Not enough parameters passed"); + return; + } + + string licenseFilePath = args[0]; + //string certificateFilePath = args[1]; + string certificateName = args[1]; + + JObject licenseFile = JObject.Parse(File.ReadAllText(licenseFilePath)); + JObject licensedFeatures = (JObject)licenseFile["LicensedFeatures"]; + + //string dataToSign = licensedFeatures.ToString(Formatting.Indented); + string dataToSign = licensedFeatures.ToString(Formatting.None); + Console.WriteLine("Signing {0}", dataToSign); + + var bytesToSign = Encoding.UTF8.GetBytes(dataToSign); + Console.WriteLine("Data to sign array length: {0}", dataToSign.Length); + + // Can't get this certificate to load the private key when it is loaded from local file system pfx + //Console.WriteLine("Specify password for {0}", certificateFilePath); + //String password = ReadPassword('*'); + //SecureString securePassword = new NetworkCredential("", password).SecurePassword; + + //X509Certificate2 privateCert = new X509Certificate2(certificateFilePath, securePassword); + + // Instead it needs to be present in the MyStore + X509Store store = new X509Store(StoreLocation.CurrentUser); + X509Certificate2 privateCert = null; + store.Open(OpenFlags.ReadOnly); + X509Certificate2Collection cers = store.Certificates.Find(X509FindType.FindBySubjectName, certificateName, false); + if (cers.Count > 0) + { + privateCert = cers[0]; + }; + store.Close(); + Console.WriteLine("Certificate: {0}", privateCert); + + var bytesSigned = SignData(privateCert, bytesToSign); + Console.WriteLine("Signed data array length: {0}", bytesSigned.Length); + + bool validated = ValidateData(privateCert, bytesSigned, bytesToSign); + Console.WriteLine("Is Validated: {0}", validated); + + string signedString = Convert.ToBase64String(bytesSigned); + Console.WriteLine("Signature {0}", signedString); + + licenseFile["Signature"] = signedString; + + Console.WriteLine("Saving {0}", licenseFile); + using (StreamWriter sw = File.CreateText(licenseFilePath)) + { + JsonSerializer serializer = new JsonSerializer(); + serializer.NullValueHandling = NullValueHandling.Include; + serializer.Formatting = Newtonsoft.Json.Formatting.Indented; + serializer.Serialize(sw, licenseFile); + } + } + + public static byte[] SignData(X509Certificate2 certificate, byte[] dataToSign) + { + var rsaPrivateKey = certificate.GetRSAPrivateKey(); + var parameters = rsaPrivateKey.ExportParameters(true); + Console.WriteLine("Certificate private key RSA parameters were successfully exported."); + + var privateKey = certificate.PrivateKey; + Console.WriteLine("Certificate private key is accessible."); + + // generate new private key in correct format + var cspParams = new CspParameters() + { + ProviderType = 24, + ProviderName = "Microsoft Enhanced RSA and AES Cryptographic Provider" + }; + var rsaCryptoServiceProvider = new RSACryptoServiceProvider(cspParams); + rsaCryptoServiceProvider.ImportParameters(parameters); + + // sign data + var signedBytes = rsaCryptoServiceProvider.SignData(dataToSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + return signedBytes; + } + + public static bool ValidateData(X509Certificate2 certificate, byte[] signature, byte[] dataToValidate) + { + var rsaPublicKey = certificate.GetRSAPublicKey(); + + bool validationResult = rsaPublicKey.VerifyData(dataToValidate, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + + return validationResult; + } + + public static string ReadPassword(char mask) + { + const int ENTER = 13, BACKSP = 8, CTRLBACKSP = 127; + int[] FILTERED = { 0, 27, 9, 10 /*, 32 space, if you care */ }; // const + + var pass = new Stack(); + char chr = (char)0; + + while ((chr = System.Console.ReadKey(true).KeyChar) != ENTER) + { + if (chr == BACKSP) + { + if (pass.Count > 0) + { + System.Console.Write("\b \b"); + pass.Pop(); + } + } + else if (chr == CTRLBACKSP) + { + while (pass.Count > 0) + { + System.Console.Write("\b \b"); + pass.Pop(); + } + } + else if (FILTERED.Count(x => chr == x) > 0) { } + else + { + pass.Push((char)chr); + System.Console.Write(mask); + } + } + + return new string(pass.Reverse().ToArray()); + } + } +} diff --git a/LicenseSign/LicenseSign/Properties/launchSettings.json b/LicenseSign/LicenseSign/Properties/launchSettings.json new file mode 100644 index 0000000..77b6032 --- /dev/null +++ b/LicenseSign/LicenseSign/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "LicenseSign": { + "commandName": "Project", + "commandLineArgs": "\"C:\\appdynamics\\AppDynamics.DEXTER\\LicenseSign\\LicenseFiles\\LicensedFeatures.standard.json\" \"AppDynamics DEXTER Licensing\"" + } + } +} \ No newline at end of file diff --git a/LicenseSign/LicenseValidate/LicenseValidate.csproj b/LicenseSign/LicenseValidate/LicenseValidate.csproj new file mode 100644 index 0000000..0eaee1b --- /dev/null +++ b/LicenseSign/LicenseValidate/LicenseValidate.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp3.1 + + + + + + + diff --git a/LicenseSign/LicenseValidate/Program.cs b/LicenseSign/LicenseValidate/Program.cs new file mode 100644 index 0000000..21a0027 --- /dev/null +++ b/LicenseSign/LicenseValidate/Program.cs @@ -0,0 +1,84 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.IO; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; + +namespace LicenseValidate +{ + class Program + { + static void Main(string[] args) + { + if (args.Length != 2) + { + Console.WriteLine("path_to_license_file path_to_certificate'"); + Console.WriteLine("Not enough parameters passed"); + return; + } + + string licenseFilePath = args[0]; + string certificateFilePath = args[1]; + //string certificateName = args[1]; + + X509Certificate2 publicCert = new X509Certificate2(certificateFilePath); + Console.WriteLine("Certificate: {0}", publicCert); + + //X509Store store = new X509Store(StoreLocation.CurrentUser); + //X509Certificate2 privateCert = null; + //store.Open(OpenFlags.ReadOnly); + //X509Certificate2Collection cers = store.Certificates.Find(X509FindType.FindBySubjectName, "AppDynamics DEXTER Licensing", false); + //if (cers.Count > 0) + //{ + // privateCert = cers[0]; + //}; + //store.Close(); + //Console.WriteLine("Certificate: {0}", privateCert); + + JObject licenseFile = JObject.Parse(File.ReadAllText(licenseFilePath)); + JObject licensedFeatures = (JObject)licenseFile["LicensedFeatures"]; + + //string dataToSign = licensedFeatures.ToString(Formatting.Indented); + string dataToSign = licensedFeatures.ToString(Formatting.None); + Console.WriteLine("Signing {0}", dataToSign); + + var bytesSigned = Encoding.UTF8.GetBytes(dataToSign); + Console.WriteLine("Data to sign array length: {0}", dataToSign.Length); + + string signatureString = licenseFile["Signature"].ToString(); + Console.WriteLine("Signature: {0}", signatureString); + + byte[] signatureBytes = Convert.FromBase64String(signatureString); + Console.WriteLine("Signature array length: {0}", signatureBytes.Length); + + if (ValidateData(publicCert, signatureBytes, bytesSigned) == true) + { + Console.WriteLine("Signature validated"); + } + else + { + Console.WriteLine("Signature invalid"); + } + + //if (ValidateData(privateCert, signatureBytes, bytesSigned) == true) + //{ + // Console.WriteLine("Signature validated"); + //} + //else + //{ + // Console.WriteLine("Signature invalid"); + //} + } + + public static bool ValidateData(X509Certificate2 certificate, byte[] signature, byte[] dataToValidate) + { + var rsaPublicKey = certificate.GetRSAPublicKey(); + + bool validationResult = rsaPublicKey.VerifyData(dataToValidate, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + + return validationResult; + } + } +} diff --git a/LicenseSign/LicenseValidate/Properties/PublishProfiles/linux-portable.pubxml b/LicenseSign/LicenseValidate/Properties/PublishProfiles/linux-portable.pubxml new file mode 100644 index 0000000..9ea4729 --- /dev/null +++ b/LicenseSign/LicenseValidate/Properties/PublishProfiles/linux-portable.pubxml @@ -0,0 +1,17 @@ + + + + + FileSystem + Release + Any CPU + netcoreapp3.1 + bin\Release\netcoreapp3.1\linux-portable + false + linux-x64 + False + False + + \ No newline at end of file diff --git a/LicenseSign/LicenseValidate/Properties/PublishProfiles/linux.pubxml b/LicenseSign/LicenseValidate/Properties/PublishProfiles/linux.pubxml new file mode 100644 index 0000000..7f930db --- /dev/null +++ b/LicenseSign/LicenseValidate/Properties/PublishProfiles/linux.pubxml @@ -0,0 +1,18 @@ + + + + + FileSystem + Release + Any CPU + netcoreapp3.1 + bin\Release\netcoreapp3.1\linux + linux-x64 + true + False + False + False + + \ No newline at end of file diff --git a/LicenseSign/LicenseValidate/Properties/PublishProfiles/windows.pubxml b/LicenseSign/LicenseValidate/Properties/PublishProfiles/windows.pubxml new file mode 100644 index 0000000..d732968 --- /dev/null +++ b/LicenseSign/LicenseValidate/Properties/PublishProfiles/windows.pubxml @@ -0,0 +1,18 @@ + + + + + FileSystem + Release + Any CPU + netcoreapp3.1 + bin\Release\netcoreapp3.1\windows\ + win-x64 + true + False + False + False + + \ No newline at end of file diff --git a/LicenseSign/LicenseValidate/Properties/launchSettings.json b/LicenseSign/LicenseValidate/Properties/launchSettings.json new file mode 100644 index 0000000..7c48617 --- /dev/null +++ b/LicenseSign/LicenseValidate/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "LicenseValidate": { + "commandName": "Project", + "commandLineArgs": "\"C:\\appdynamics\\AppDynamics.DEXTER\\LicenseSign\\LicenseFiles\\LicensedFeatures.standard.json\" \"C:\\appdynamics\\AppDynamics.DEXTER\\src\\AppDynamics.DEXTER.public.cer\"" + } + } +} \ No newline at end of file diff --git a/LicenseSign/Readme.txt b/LicenseSign/Readme.txt new file mode 100644 index 0000000..7d11322 --- /dev/null +++ b/LicenseSign/Readme.txt @@ -0,0 +1,15 @@ +First, issue the new certificate via running IssueCertificate console exe +That is in .NET 4.7 so windows only +Somehow when you issue it this way, export and then reimport by hand the private key is still readable + +BTW +Tried issuing certificates via PowerShell (IssueCertPS) and it works, but when exported and then reimported, the private key +is not parseable + +Second, import the certificate into My personal store. Only from there will it have private key readable + +Third, sign the license file via LicenseSign +This is .NET Core 3.1 +Uses private key from the cert in My personal store to sign the license file + +Fourth, read the license file and validate signature from DEXTER \ No newline at end of file diff --git a/LicensedFeatures.json b/LicensedFeatures.json index ed1798b..3d5314a 100644 --- a/LicensedFeatures.json +++ b/LicensedFeatures.json @@ -1,5 +1,5 @@ { - "Signature": "jeBQXY+Bum3Lt3hA5kuYfnff/NLQgRj6NwLbMphjU2Sr1Stz9RlR2U3Qy2DScXWNv4Li42VwhszJhKOdTKbHt3U9YtuavyW69wVSrExX9D67wJuuOi08cOVaCwuEiYJSiISUMCn7rZ9gjCy+2QBfAVkAFMpjSWx0YHDpKyMir5fVxO9Ulv3nCXmz0C/lMpiiOpUlWQfPV+B0agRpEtHvcEfg8zvpLW5Bhmbii0I+UqWn4rdcraG2aXFGKb5AsY47UxUjzI+ihuJQ+HzkI9Lo7CraBNiaRp/R5qfI57Q/9l5vOQPVZMhogSBjwgjzvuYh6hr443N6olHF/yjUY1RfxA==", + "Signature": "Hhs4xVSGQvoeAm2UrImV/nD/2HEqYyiNI/3wgQz10gDVGQ9IajjiC9GZV2Zrnc533vVPw5lZP260/faTj1OIT+BkD3d+FxGIl3tyv02zPNCJ3gRO1VIHbcHcMeNMLdeh5CXHZq7QiHZjuqG7c27L5v5/QD9fawSO7mVUYWO8Z2p74VgX3YVIV8xAv5WdkL8+qa78PDvoWGT7O8//jpoEL4gOwqa07pN5CwftqMAdu3/qFQGNapLYES3COIyJlufByl03xSlA+picO65BMAjoVeCvNOW+7qH5xtm2dAUYj7RCRQDbeGNODM/lucJK3SJVFbJ0BtMTetUCsRDlOkIA+w==", "LicensedFeatures": { "ExpirationDateTime": "2031-01-01T00:00:00Z", "ApplicationSummary": false, @@ -10,6 +10,7 @@ "EntityDetails": true, "EntityMetricGraphs": true, "EntityMetrics": true, + "Flowmaps": true, "Events": true, "FlameGraphs": true, "HealthCheck": false, diff --git a/MethodNamespaceTypeMapping.csv b/MethodNamespaceTypeMapping.csv index 390fff7..de7a322 100644 --- a/MethodNamespaceTypeMapping.csv +++ b/MethodNamespaceTypeMapping.csv @@ -3,12 +3,14 @@ ActivityFeeds,Microsoft Dynamics CRM,ED5139,E8998D,000000 Amazon.Runtime,Amazon Core,FF9900,FBBB5B,000000 antlr,ANTLR Parser,a265b8,b47bc9,000000 AngleSharp,AngleSharp DOM Parser,a265b8,b47bc9,000000 +ariba.ui.aribaweb,Ariba Web,a265b8,b47bc9,000000 ASP,.NET ASP.NET,6197FF,94C9FF,000000 Aspose.Pdf,Aspose Components,a16f6f,b87b7b,FFFFFF Asynchronous,.NET ASP.NET,6197FF,94C9FF,000000 Autofac,Autofac IoC,140fb8,1c15d6,FFFFFF AutoMapper,AutoMapper,bba6bf,d7c3db,000000 Castle,Castle Project,e09b9b,fab9b9,000000 +CrystalDecisions,Crystal Decisions,36ffc3,9ffce0,000000 ch.qos.logback,Logback,ff875e,ff9a78,000000 com.amazonaws,Amazon Core,FF9900,FBBB5B,000000 com.amazonaws.AmazonWebServiceClient,Amazon Web Service Client,FF9900,FBBB5B,000000 @@ -53,13 +55,16 @@ com.ibm,IBM Core,00BFA9,35CCBA,000000 com.inet.ora,Inet Oracle JDBC Driver,36ffc3,9ffce0,000000 com.informatica,Informatica,62a9e3,6db9f7,000000 com.informix,Informix,6ae6b6,7ff5c8,000000 +com.jnetdirect,JNetDirect JDBC Driver,6ae6b6,7ff5c8,000000 com.matrixone,3DS MatrixOne,ff1919,ff5959,FFFFFF com.mchange.v2.c3p0,c3p0 JDBC Driver,36ffc3,9ffce0,000000 com.mchange.v2.resourcepool,c3p0 JDBC Driver Pooling,36ffc3,9ffce0,000000 com.microsoft,Microsoft,00A1F1,4FBAF0,000000 com.microsoft.sqlserver,Microsoft SQL Server Driver,00A1F1,4FBAF0,000000 com.mongodb,Mongo DB,633838,854949,FFFFFF +com.moogsoft,Moogsoft,633838,854949,FFFFFF com.mule,Mule ESB,e3b2ed,f4cafc,000000 +com.mulesoft,Mule ESB,e3b2ed,f4cafc,000000 com.mysql,MySQL Driver,36ffc3,9ffce0,000000 com.netflix.hystrix,Hystrix Fault Tolerance,633838,854949,FFFFFF com.newrelic,NewRelic,7668CF,978FCF,FFFFFF @@ -69,10 +74,14 @@ com.oracle,Oracle Core,f80000,fa3434,FFFFFF com.paypal,PayPal,70dbb9,88ebcb,000000 com.pega,Pega,b2b6db,c2c7f0,000000 com.pegarules,Pega,b2b6db,c2c7f0,000000 +com.pingidentity,Ping Identity,b2b6db,c2c7f0,000000 com.pointbase,Pointbase Driver,828156,94936d,FFFFFF com.rabbitmq,Rabbit MQ,bcccc6,d5e3de,000000 com.rsa,RSA Security,8bc4af,9ad9c2,000000 com.singularity,AppDynamics,7668CF,978FCF,FFFFFF +com.sap.engine,SAP Engine,bcccc6,d5e3de,000000 +com.sap.portal,SAP Portal,bcccc6,d5e3de,000000 +com.sapportals,SAP Portal,bcccc6,d5e3de,000000 com.softwareag,Software AG,bcccc6,d5e3de,000000 com.sun,JDK Core,75E500,BBFA38,000000 com.sun.jersey,Jersey REST Framework,75E500,BBFA38,000000 @@ -84,9 +93,11 @@ com.teradata,Teradata Database,12588b,226494,FFFFFF com.terracotta,Terracotta DB,12588b,226494,FFFFFF com.terracottatech,Terracotta DB,12588b,226494,FFFFFF com.thoughtworks,ThoughtWorks,e874d9,ff82ee,000000 +com.tibco,TIBCO Core,12588b,226494,FFFFFF com.webex,Cisco Webex,418521,4B9E24,FFFFFF com.weblogic,WebLogic Core,05f0e0,37ede1,000000 com.webmethods,Software AG webMethods,40ff96,6effaf,000000 +com.uc4,Broadcom Automic,bcccc6,d5e3de,000000 Corillian,Corillian/Voyager Banking,fcf644,fcf760,000000 custom01,Custom Mapping for Flame Graphs,fcf644,fcf760,000000 custom02,Custom Mapping for Flame Graphs,c4d14d,e1f059,000000 @@ -121,6 +132,7 @@ io.github.resilience4j,Resilience4j Fault Tolerance,8abfb9,a7d6d1,000000 io.lettuce,Lettuce Redis Client,BA90FC,C5A7FC,000000 io.netty,Netty NIO,993006,b5471b,FFFFFF io.prometheus,Prometheus,BA90FC,C5A7FC,000000 +io.reactivex,Reactive RxJava,e4ff36,ecff73,000000 io.undertow,JBoss Undertow,99a30d,acb52a,000000 Ionic.Crc,Ionic,c5d186,e1f099,000000 Ionic.Zlib,Ionic,c5d186,e1f099,000000 @@ -128,6 +140,7 @@ java,Java Core,75E500,BBFA38,000000 java.io,Java IO,75E500,BBFA38,000000 java.lang.reflect,Java Reflection,75E500,BBFA38,000000 javax,Java Extension,75E500,BBFA38,000000 +jdk.internal.reflect,Java Reflection,75E500,BBFA38,000000 jdk.internal.dynalink,Oracle Nashorn,f80000,fa3434,FFFFFF jdk.nashorn,Oracle Nashorn,f80000,fa3434,FFFFFF jdk.xml,Java XML,75E500,BBFA38,000000 @@ -160,7 +173,8 @@ MS.Internal.Xml,.NET XML,6197FF,94C9FF,000000 MySQL,MySQL Driver,36ffc3,9ffce0,000000 netegrity.siteminder,Siteminder Netegrity,aca644,aca760,FFFFFF net.sf,SourceForge,FE5810,FF8754,000000 -netscape,Netscape,558c07,6aa813,FFFFFF +net.sourceforge.jtds,MySQL Driver,36ffc3,9ffce0,000000 +netscape,JTDS JDBC Driver,558c07,6aa813,FFFFFF Newtonsoft.Json,Newtonsoft JSON,a3f57f,bffca4,000000 NHibernate,NHibernate ORM,a3f57f,bffca4,000000 NLog,NLog Framework,a2d9f2,bfebff,000000 @@ -185,10 +199,11 @@ org.eclipse.jetty,Eclipse Jetty,e3396f,f25587,000000 org.glassfish,GlassFish,bb9bc7,d8b3e6,000000 org.jboss,JBoss,99a30d,acb52a,FFFFFF org.jboss.soa.esb.client,JBoss ESB,99a30d,acb52a,FFFFFF -org.mule.processor,Mule ESB,FE5810,FF8754,000000 +org.mule,Mule ESB,FE5810,FF8754,000000 org.springframework,Spring,6DB33F,9ACE78,000000 org.springframework.integration,Spring Integration,6DB33F,9ACE78,000000 org.springframework.jms,Spring JMS,6DB33F,9ACE78,000000 +org.quartz,Quartz Job Scheduler,99a30d,acb52a,FFFFFF persistence.antlr,TopLink JPA,f20ab0,f549c4,000000 RazorEngine,Antaris RazorEngine,ad5fc9,c169e0,000000 redis,Redis Cache,ff4d4d,ff9696,FFFFFF @@ -199,6 +214,8 @@ rx.internal,ReactiveX Observable Async,99a30d,acb52a,FFFFFF rx.Observable,ReactiveX Observable Async,99a30d,acb52a,FFFFFF rx.observables,ReactiveX Observable Async,99a30d,acb52a,FFFFFF rx.Subscriber,ReactiveX Observable Async,99a30d,acb52a,FFFFFF +scala,Scala Runtime,FD6DEE,F5CCE7,000000 +SD.LLBLGen,LLBLGen ORM,FD6DEE,F5CCE7,000000 Sitecore,Sitecore CMS,8abfb9,a7d6d1,000000 SNINativeMethodWrapper,.NET ADO.NET,6197FF,94C9FF,000000 StackExchange.Redis,Redis StackExchange,ff4d4d,ff9696,FFFFFF @@ -215,6 +232,7 @@ System.Diagnostics,.NET Diagnostics,6197FF,94C9FF,000000 System.IO,.NET IO,6197FF,94C9FF,000000 System.Linq,.NET Linq,6197FF,94C9FF,000000 System.Net,.NET Network,6197FF,94C9FF,000000 +System.Reactive,Reactive Rx.NET,e4ff36,ecff73,000000 System.Reflection,.NET Reflection,6197FF,94C9FF,000000 System.Runtime,.NET Runtime,6197FF,94C9FF,000000 System.Security,.NET Security,6197FF,94C9FF,000000 diff --git a/MethodNamespaceTypeMapping.xlsm b/MethodNamespaceTypeMapping.xlsm index 552161e..d8c547f 100644 Binary files a/MethodNamespaceTypeMapping.xlsm and b/MethodNamespaceTypeMapping.xlsm differ diff --git a/ProcessingSteps/Extract/ExtractAPMConfiguration.cs b/ProcessingSteps/Extract/ExtractAPMConfiguration.cs index 0c09535..41435a2 100644 --- a/ProcessingSteps/Extract/ExtractAPMConfiguration.cs +++ b/ProcessingSteps/Extract/ExtractAPMConfiguration.cs @@ -38,6 +38,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_APM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + return true; } diff --git a/ProcessingSteps/Extract/ExtractAPMEntities.cs b/ProcessingSteps/Extract/ExtractAPMEntities.cs index 189ea69..6cfd260 100644 --- a/ProcessingSteps/Extract/ExtractAPMEntities.cs +++ b/ProcessingSteps/Extract/ExtractAPMEntities.cs @@ -45,6 +45,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_APM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + return true; } @@ -131,9 +134,19 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job var listOfBackendsInHourChunks = backendsList.BreakListIntoChunks(ENTITIES_EXTRACT_NUMBER_OF_BACKENDS_TO_PROCESS_PER_THREAD); + ParallelOptions parallelOptions = new ParallelOptions(); + if (programOptions.ProcessSequentially == true) + { + parallelOptions.MaxDegreeOfParallelism = 1; + } + else + { + parallelOptions.MaxDegreeOfParallelism = BACKEND_PROPERTIES_EXTRACT_NUMBER_OF_THREADS; + } + Parallel.ForEach, int>( listOfBackendsInHourChunks, - new ParallelOptions { MaxDegreeOfParallelism = BACKEND_PROPERTIES_EXTRACT_NUMBER_OF_THREADS }, + parallelOptions, () => 0, (listOfBackendsInHourChunk, loop, subtotal) => { @@ -222,9 +235,19 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job var listOfNodesInHourChunks = nodesList.BreakListIntoChunks(ENTITIES_EXTRACT_NUMBER_OF_NODES_TO_PROCESS_PER_THREAD); + ParallelOptions parallelOptions = new ParallelOptions(); + if (programOptions.ProcessSequentially == true) + { + parallelOptions.MaxDegreeOfParallelism = 1; + } + else + { + parallelOptions.MaxDegreeOfParallelism = NODE_PROPERTIES_EXTRACT_NUMBER_OF_THREADS; + } + Parallel.ForEach, int>( listOfNodesInHourChunks, - new ParallelOptions { MaxDegreeOfParallelism = NODE_PROPERTIES_EXTRACT_NUMBER_OF_THREADS }, + parallelOptions, () => 0, (listOfNodesInHourChunk, loop, subtotal) => { diff --git a/ProcessingSteps/Extract/ExtractAPMEntityDashboardScreenshots.cs b/ProcessingSteps/Extract/ExtractAPMEntityDashboardScreenshots.cs index 2a2a207..50ccb7f 100644 --- a/ProcessingSteps/Extract/ExtractAPMEntityDashboardScreenshots.cs +++ b/ProcessingSteps/Extract/ExtractAPMEntityDashboardScreenshots.cs @@ -1,21 +1,19 @@ using AppDynamics.Dexter.DataObjects; using AppDynamics.Dexter.ReportObjectMaps; using AppDynamics.Dexter.ReportObjects; +using NLog; +using OpenQA.Selenium; +using OpenQA.Selenium.Chrome; +using OpenQA.Selenium.Remote; +using OpenQA.Selenium.Support.UI; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using OpenQA.Selenium; -using OpenQA.Selenium.Chrome; -using OpenQA.Selenium.Remote; -using OpenQA.Selenium.Support.UI; -using NLog; using System.Runtime.InteropServices; -using System.Reflection; using System.Text.RegularExpressions; +using System.Threading; namespace AppDynamics.Dexter.ProcessingSteps { @@ -59,6 +57,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_APM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + return true; } @@ -549,14 +550,17 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job foreach (AppDRESTTier tier in tiersList) { // Filter Tier type - if (jobConfiguration.Input.EntityDashboardSelectionCriteria.TierType.All != true) + string allElement = Array.Find(jobConfiguration.Input.EntityDashboardSelectionCriteria.TierTypes, e => e.ToLower() == "all"); + if (allElement != null && allElement.ToLower() == "all") { - PropertyInfo pi = jobConfiguration.Input.EntityDashboardSelectionCriteria.TierType.GetType().GetProperty(tier.agentType); - if (pi != null) - { - if ((bool)pi.GetValue(jobConfiguration.Input.EntityDashboardSelectionCriteria.TierType) == false) continue; - } + // All tiers are to be kept } + else + { + string entityTypeElement = Array.Find(jobConfiguration.Input.EntityDashboardSelectionCriteria.TierTypes, e => e.ToUpper() == tier.agentType); + if (entityTypeElement == null) continue; + } + // Filter Tier name bool tierNameMatch = false; if (jobConfiguration.Input.EntityDashboardSelectionCriteria.Tiers.Length == 0) tierNameMatch = true; @@ -628,14 +632,17 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job foreach (AppDRESTNode node in nodesList) { // Filter Node type - if (jobConfiguration.Input.EntityDashboardSelectionCriteria.NodeType.All != true) + string allElement = Array.Find(jobConfiguration.Input.EntityDashboardSelectionCriteria.NodeTypes, e => e.ToLower() == "all"); + if (allElement != null && allElement.ToLower() == "all") { - PropertyInfo pi = jobConfiguration.Input.EntityDashboardSelectionCriteria.NodeType.GetType().GetProperty(node.agentType); - if (pi != null) - { - if ((bool)pi.GetValue(jobConfiguration.Input.EntityDashboardSelectionCriteria.NodeType) == false) continue; - } + // All tiers are to be kept + } + else + { + string entityTypeElement = Array.Find(jobConfiguration.Input.EntityDashboardSelectionCriteria.NodeTypes, e => e.ToUpper() == node.agentType); + if (entityTypeElement == null) continue; } + // Filter Node name bool nodeNameMatch = false; if (jobConfiguration.Input.EntityDashboardSelectionCriteria.Nodes.Length == 0) nodeNameMatch = true; @@ -707,14 +714,17 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job foreach (AppDRESTBusinessTransaction businessTransaction in businessTransactionsList) { // Filter Business Transaction type - if (jobConfiguration.Input.EntityDashboardSelectionCriteria.BusinessTransactionType.All != true) + string allElement = Array.Find(jobConfiguration.Input.EntityDashboardSelectionCriteria.BusinessTransactionTypes, e => e.ToLower() == "all"); + if (allElement != null && allElement.ToLower() == "all") { - PropertyInfo pi = jobConfiguration.Input.EntityDashboardSelectionCriteria.BusinessTransactionType.GetType().GetProperty(businessTransaction.entryPointType); - if (pi != null) - { - if ((bool)pi.GetValue(jobConfiguration.Input.EntityDashboardSelectionCriteria.BusinessTransactionType) == false) continue; - } + // All tiers are to be kept + } + else + { + string entityTypeElement = Array.Find(jobConfiguration.Input.EntityDashboardSelectionCriteria.BusinessTransactionTypes, e => e.ToUpper() == businessTransaction.entryPointType); + if (entityTypeElement == null) continue; } + // Filter Business Transaction name bool businessTransactionNameMatch = false; if (jobConfiguration.Input.EntityDashboardSelectionCriteria.BusinessTransactions.Length == 0) businessTransactionNameMatch = true; @@ -786,14 +796,17 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job foreach (AppDRESTBackend backend in backendsList) { // Filter Backend type - if (jobConfiguration.Input.EntityDashboardSelectionCriteria.BackendType.All != true) + string allElement = Array.Find(jobConfiguration.Input.EntityDashboardSelectionCriteria.BackendTypes, e => e.ToLower() == "all"); + if (allElement != null && allElement.ToLower() == "all") { - PropertyInfo pi = jobConfiguration.Input.EntityDashboardSelectionCriteria.BackendType.GetType().GetProperty(backend.exitPointType); - if (pi != null) - { - if ((bool)pi.GetValue(jobConfiguration.Input.EntityDashboardSelectionCriteria.BackendType) == false) continue; - } + // All tiers are to be kept } + else + { + string entityTypeElement = Array.Find(jobConfiguration.Input.EntityDashboardSelectionCriteria.BackendTypes, e => e.ToUpper() == backend.exitPointType); + if (entityTypeElement == null) continue; + } + // Filter Backend name bool backendNameMatch = false; if (jobConfiguration.Input.EntityDashboardSelectionCriteria.Backends.Length == 0) backendNameMatch = true; diff --git a/ProcessingSteps/Extract/ExtractAPMFlowmaps.cs b/ProcessingSteps/Extract/ExtractAPMFlowmaps.cs index 60bddc3..2b299c8 100644 --- a/ProcessingSteps/Extract/ExtractAPMFlowmaps.cs +++ b/ProcessingSteps/Extract/ExtractAPMFlowmaps.cs @@ -40,6 +40,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_APM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + return true; } @@ -80,7 +83,13 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job #endregion - Parallel.Invoke( + ParallelOptions parallelOptions = new ParallelOptions(); + if (programOptions.ProcessSequentially == true) + { + parallelOptions.MaxDegreeOfParallelism = 1; + } + + Parallel.Invoke(parallelOptions, () => { #region Application diff --git a/ProcessingSteps/Extract/ExtractAPMMetrics.cs b/ProcessingSteps/Extract/ExtractAPMMetrics.cs index aea48da..ec67d81 100644 --- a/ProcessingSteps/Extract/ExtractAPMMetrics.cs +++ b/ProcessingSteps/Extract/ExtractAPMMetrics.cs @@ -36,6 +36,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_APM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + return true; } @@ -72,7 +75,13 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job loggerConsole.Info("Extract Metrics for All Entities ({0} time ranges)", jobConfiguration.Input.HourlyTimeRanges.Count); - Parallel.Invoke( + ParallelOptions parallelOptions = new ParallelOptions(); + if (programOptions.ProcessSequentially == true) + { + parallelOptions.MaxDegreeOfParallelism = 1; + } + + Parallel.Invoke(parallelOptions, () => { #region Application diff --git a/ProcessingSteps/Extract/ExtractAPMSnapshots.cs b/ProcessingSteps/Extract/ExtractAPMSnapshots.cs index bc98e13..09e0b55 100644 --- a/ProcessingSteps/Extract/ExtractAPMSnapshots.cs +++ b/ProcessingSteps/Extract/ExtractAPMSnapshots.cs @@ -9,7 +9,6 @@ using System.Diagnostics; using System.IO; using System.Linq; -using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading; @@ -49,6 +48,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_APM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + return true; } @@ -299,19 +301,19 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job switch (snapshotToken["userExperience"].ToString()) { case "NORMAL": - if (jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience.Normal != true) continue; + if (jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience.Normal != true) keepSnapshot = false; break; case "SLOW": - if (jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience.Slow != true) continue; + if (jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience.Slow != true) keepSnapshot = false; break; case "VERY_SLOW": - if (jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience.VerySlow != true) continue; + if (jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience.VerySlow != true) keepSnapshot = false; break; case "STALL": - if (jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience.Stall != true) continue; + if (jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience.Stall != true) keepSnapshot = false; break; case "ERROR": - if (jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience.Error != true) continue; + if (jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience.Error != true) keepSnapshot = false; break; default: // Not sure what kind of beast it is @@ -320,7 +322,7 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job } if (keepSnapshot == false) { - logger.Trace("Filtering snapshot requestGUID={0} because its user experience is unknown", snapshotToken["requestGUID"]); + logger.Trace("Filtering snapshot requestGUID={0} because of it's user experience", snapshotToken["requestGUID"]); continue; } @@ -339,51 +341,61 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job } if (keepSnapshot == false) { - logger.Trace("Filtering snapshot requestGUID={0} because its of call graph criteria didn't match", snapshotToken["requestGUID"]); + logger.Trace("Filtering snapshot requestGUID={0} because of it's call graph criteria didn't match", snapshotToken["requestGUID"]); continue; } // Filter Tier type - if (jobConfiguration.Input.SnapshotSelectionCriteria.TierType.All != true) + string allElement = Array.Find(jobConfiguration.Input.SnapshotSelectionCriteria.TierTypes, e => e.ToLower() == "all"); + if (allElement != null && allElement.ToLower() == "all") + { + // All tiers are to be kept + } + else { if (tiersList != null) { AppDRESTTier tier = tiersList.Where(t => t.id == (long)snapshotToken["applicationComponentId"]).FirstOrDefault(); if (tier != null) { - PropertyInfo pi = jobConfiguration.Input.SnapshotSelectionCriteria.TierType.GetType().GetProperty(tier.agentType); - if (pi != null) + string entityTypeElement = Array.Find(jobConfiguration.Input.SnapshotSelectionCriteria.TierTypes, e => e.ToUpper() == tier.agentType); + if (entityTypeElement == null) { - if ((bool)pi.GetValue(jobConfiguration.Input.SnapshotSelectionCriteria.TierType) == false) keepSnapshot = false; + keepSnapshot = false; } } } } if (keepSnapshot == false) { - logger.Trace("Filtering snapshot requestGUID={0} because its of tier type", snapshotToken["requestGUID"]); + logger.Trace("Filtering snapshot requestGUID={0} because of it's tier type", snapshotToken["requestGUID"]); continue; } // Filter BT type - if (jobConfiguration.Input.SnapshotSelectionCriteria.BusinessTransactionType.All != true) + allElement = Array.Find(jobConfiguration.Input.SnapshotSelectionCriteria.BusinessTransactionTypes, e => e.ToLower() == "all"); + if (allElement != null && allElement.ToLower() == "all") + { + // All BTs are to be kept + } + else { if (businessTransactionsList != null) { AppDRESTBusinessTransaction businessTransaction = businessTransactionsList.Where(b => b.id == (long)snapshotToken["businessTransactionId"] && b.tierId == (long)snapshotToken["applicationComponentId"]).FirstOrDefault(); if (businessTransaction != null) { - PropertyInfo pi = jobConfiguration.Input.SnapshotSelectionCriteria.BusinessTransactionType.GetType().GetProperty(businessTransaction.entryPointType); - if (pi != null) + string entityTypeElement = Array.Find(jobConfiguration.Input.SnapshotSelectionCriteria.BusinessTransactionTypes, e => e.ToUpper() == businessTransaction.entryPointType); + if (entityTypeElement == null) { - if ((bool)pi.GetValue(jobConfiguration.Input.SnapshotSelectionCriteria.BusinessTransactionType) == false) keepSnapshot = false; + keepSnapshot = false; } } } } if (keepSnapshot == false) { - logger.Trace("Filtering snapshot requestGUID={0} because its of business transaction type", snapshotToken["requestGUID"]); + logger.Trace("Filtering snapshot requestGUID={0} because of it's business transaction type", snapshotToken["requestGUID"]); continue; } diff --git a/ProcessingSteps/Extract/ExtractApplicationEventsAndHealthRuleViolations.cs b/ProcessingSteps/Extract/ExtractApplicationEventsAndHealthRuleViolations.cs index 218a169..fc7290f 100644 --- a/ProcessingSteps/Extract/ExtractApplicationEventsAndHealthRuleViolations.cs +++ b/ProcessingSteps/Extract/ExtractApplicationEventsAndHealthRuleViolations.cs @@ -149,9 +149,19 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job loggerConsole.Info("Extract {0} event types out of possible {1} ({2} time ranges)", eventTypesToRetrieve.Count, EVENT_TYPES.Count, jobConfiguration.Input.HourlyTimeRanges.Count); + ParallelOptions parallelOptions = new ParallelOptions(); + if (programOptions.ProcessSequentially == true) + { + parallelOptions.MaxDegreeOfParallelism = 1; + } + else + { + parallelOptions.MaxDegreeOfParallelism = EVENTS_EXTRACT_NUMBER_OF_THREADS; + } + Parallel.ForEach( eventTypesToRetrieve, - new ParallelOptions { MaxDegreeOfParallelism = EVENTS_EXTRACT_NUMBER_OF_THREADS }, + parallelOptions, () => 0, (eventType, loop, subtotal) => { @@ -189,7 +199,7 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job Parallel.ForEach, int>( listOfEventsInChunks, - new ParallelOptions { MaxDegreeOfParallelism = EVENTS_EXTRACT_NUMBER_OF_THREADS }, + parallelOptions, () => 0, (listOfEventsChunk, loop, subtotal) => { diff --git a/ProcessingSteps/Extract/ExtractBIQConfiguration.cs b/ProcessingSteps/Extract/ExtractBIQConfiguration.cs index 21ad8bd..5fec40a 100644 --- a/ProcessingSteps/Extract/ExtractBIQConfiguration.cs +++ b/ProcessingSteps/Extract/ExtractBIQConfiguration.cs @@ -34,6 +34,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_BIQ) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_BIQ); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_BIQ); + return true; } diff --git a/ProcessingSteps/Extract/ExtractBIQEntities.cs b/ProcessingSteps/Extract/ExtractBIQEntities.cs index d10423e..539f6a1 100644 --- a/ProcessingSteps/Extract/ExtractBIQEntities.cs +++ b/ProcessingSteps/Extract/ExtractBIQEntities.cs @@ -36,6 +36,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_BIQ) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_BIQ); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_BIQ); + return true; } @@ -67,7 +70,6 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job // Set up controller access using (ControllerApi controllerApi = new ControllerApi(jobTarget.Controller, jobTarget.UserName, AESEncryptionHelper.Decrypt(jobTarget.UserPassword))) { - controllerApi.PrivateApiLogin(); #region Prepare time range diff --git a/ProcessingSteps/Extract/ExtractDBConfiguration.cs b/ProcessingSteps/Extract/ExtractDBConfiguration.cs index cf17f03..9b59729 100644 --- a/ProcessingSteps/Extract/ExtractDBConfiguration.cs +++ b/ProcessingSteps/Extract/ExtractDBConfiguration.cs @@ -34,6 +34,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_DB) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_DB); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_DB); + return true; } diff --git a/ProcessingSteps/Extract/ExtractDBEntities.cs b/ProcessingSteps/Extract/ExtractDBEntities.cs index 0bd0cfc..e3498fe 100644 --- a/ProcessingSteps/Extract/ExtractDBEntities.cs +++ b/ProcessingSteps/Extract/ExtractDBEntities.cs @@ -37,6 +37,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_DB) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_DB); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_DB); + return true; } @@ -69,6 +72,7 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job Version version4_5 = new Version(4, 5); Version version4_4 = new Version(4, 4); + Version version20_4 = new Version(20, 4); Version versionThisController = new Version(jobTarget.ControllerVersion); #endregion @@ -125,7 +129,13 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job // Check the file existence to support resuming previously interrupted jobs if (File.Exists(FilePathMap.DBQueriesDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)) == false) { - Parallel.Invoke( + ParallelOptions parallelOptions = new ParallelOptions(); + if (programOptions.ProcessSequentially == true) + { + parallelOptions.MaxDegreeOfParallelism = 1; + } + + Parallel.Invoke(parallelOptions, () => { using (ControllerApi controllerApiParallel = new ControllerApi(jobTarget.Controller, jobTarget.UserName, AESEncryptionHelper.Decrypt(jobTarget.UserPassword))) @@ -137,14 +147,19 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (allWaitStatesJSON != String.Empty) FileIOHelper.SaveFileToPath(allWaitStatesJSON, FilePathMap.DBAllWaitStatesDataFilePath(jobTarget)); loggerConsole.Info("Current Wait States"); - if (versionThisController >= version4_5) + if (versionThisController >= version20_4) + { + string currentWaitStatesJSON = controllerApiParallel.GetDCurrentWaitStates_20_4(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, differenceInMinutes); + if (currentWaitStatesJSON != String.Empty) FileIOHelper.SaveFileToPath(currentWaitStatesJSON, FilePathMap.DBCurrentWaitStatesDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); + } + else if (versionThisController >= version4_5) { - string currentWaitStatesJSON = controllerApiParallel.GetDCurrentWaitStates(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, differenceInMinutes, "4.5"); + string currentWaitStatesJSON = controllerApiParallel.GetDCurrentWaitStates_4_5(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, differenceInMinutes); if (currentWaitStatesJSON != String.Empty) FileIOHelper.SaveFileToPath(currentWaitStatesJSON, FilePathMap.DBCurrentWaitStatesDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); } else if (versionThisController >= version4_4) { - string currentWaitStatesJSON = controllerApiParallel.GetDCurrentWaitStates(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, differenceInMinutes, "4.4"); + string currentWaitStatesJSON = controllerApiParallel.GetDCurrentWaitStates_4_4(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, differenceInMinutes); if (currentWaitStatesJSON != String.Empty) FileIOHelper.SaveFileToPath(currentWaitStatesJSON, FilePathMap.DBCurrentWaitStatesDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); } } @@ -156,26 +171,36 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job controllerApiParallel.PrivateApiLogin(); loggerConsole.Info("Queries"); - if (versionThisController >= version4_5) + if (versionThisController >= version20_4) + { + string queriesJSON = controllerApiParallel.GetDBQueries_20_4(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); + if (queriesJSON != String.Empty) FileIOHelper.SaveFileToPath(queriesJSON, FilePathMap.DBQueriesDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); + } + else if (versionThisController >= version4_5) { - string queriesJSON = controllerApiParallel.GetDBQueries(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, "4.5"); + string queriesJSON = controllerApiParallel.GetDBQueries_4_5(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); if (queriesJSON != String.Empty) FileIOHelper.SaveFileToPath(queriesJSON, FilePathMap.DBQueriesDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); } else if (versionThisController >= version4_4) { - string queriesJSON = controllerApiParallel.GetDBQueries(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, "4.4"); + string queriesJSON = controllerApiParallel.GetDBQueries_4_4(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); if (queriesJSON != String.Empty) FileIOHelper.SaveFileToPath(queriesJSON, FilePathMap.DBQueriesDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); } loggerConsole.Info("Clients"); - if (versionThisController >= version4_5) + if (versionThisController >= version20_4) { - string clientsJSON = controllerApiParallel.GetDBClients(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, "4.5"); + string clientsJSON = controllerApiParallel.GetDBClients_20_4(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); + if (clientsJSON != String.Empty) FileIOHelper.SaveFileToPath(clientsJSON, FilePathMap.DBClientsDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); + } + else if (versionThisController >= version4_5) + { + string clientsJSON = controllerApiParallel.GetDBClients_4_5(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); if (clientsJSON != String.Empty) FileIOHelper.SaveFileToPath(clientsJSON, FilePathMap.DBClientsDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); } else if (versionThisController >= version4_4) { - string clientsJSON = controllerApiParallel.GetDBClients(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, "4.4"); + string clientsJSON = controllerApiParallel.GetDBClients_4_4(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); if (clientsJSON != String.Empty) FileIOHelper.SaveFileToPath(clientsJSON, FilePathMap.DBClientsDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); } } @@ -187,38 +212,53 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job controllerApiParallel.PrivateApiLogin(); loggerConsole.Info("Sessions"); - if (versionThisController >= version4_5) + if (versionThisController >= version20_4) + { + string sessionsJSON = controllerApiParallel.GetDBSessions_20_4(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); + if (sessionsJSON != String.Empty) FileIOHelper.SaveFileToPath(sessionsJSON, FilePathMap.DBSessionsDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); + } + else if (versionThisController >= version4_5) { - string sessionsJSON = controllerApiParallel.GetDBSessions(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, "4.5"); + string sessionsJSON = controllerApiParallel.GetDBSessions_4_5(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); if (sessionsJSON != String.Empty) FileIOHelper.SaveFileToPath(sessionsJSON, FilePathMap.DBSessionsDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); } else if (versionThisController >= version4_4) { - string sessionsJSON = controllerApiParallel.GetDBSessions(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, "4.4"); + string sessionsJSON = controllerApiParallel.GetDBSessions_4_4(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); if (sessionsJSON != String.Empty) FileIOHelper.SaveFileToPath(sessionsJSON, FilePathMap.DBSessionsDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); } loggerConsole.Info("Databases"); - if (versionThisController >= version4_5) + if (versionThisController >= version20_4) { - string databasesJSON = controllerApiParallel.GetDBDatabases(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, "4.5"); + string databasesJSON = controllerApiParallel.GetDBDatabases_20_4(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); + if (databasesJSON != String.Empty) FileIOHelper.SaveFileToPath(databasesJSON, FilePathMap.DBDatabasesDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); + } + else if (versionThisController >= version4_5) + { + string databasesJSON = controllerApiParallel.GetDBDatabases_4_5(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); if (databasesJSON != String.Empty) FileIOHelper.SaveFileToPath(databasesJSON, FilePathMap.DBDatabasesDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); } else if (versionThisController >= version4_4) { - string databasesJSON = controllerApiParallel.GetDBDatabases(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, "4.4"); + string databasesJSON = controllerApiParallel.GetDBDatabases_4_4(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); if (databasesJSON != String.Empty) FileIOHelper.SaveFileToPath(databasesJSON, FilePathMap.DBDatabasesDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); } loggerConsole.Info("Users"); - if (versionThisController >= version4_5) + if (versionThisController >= version20_4) { - string usersJSON = controllerApiParallel.GetDBUsers(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, "4.5"); + string usersJSON = controllerApiParallel.GetDBUsers_20_4(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); + if (usersJSON != String.Empty) FileIOHelper.SaveFileToPath(usersJSON, FilePathMap.DBUsersDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); + } + else if (versionThisController >= version4_5) + { + string usersJSON = controllerApiParallel.GetDBUsers_4_5(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); if (usersJSON != String.Empty) FileIOHelper.SaveFileToPath(usersJSON, FilePathMap.DBUsersDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); } else if (versionThisController >= version4_4) { - string usersJSON = controllerApiParallel.GetDBUsers(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, "4.4"); + string usersJSON = controllerApiParallel.GetDBUsers_4_4(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); if (usersJSON != String.Empty) FileIOHelper.SaveFileToPath(usersJSON, FilePathMap.DBUsersDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); } } @@ -230,32 +270,50 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job controllerApiParallel.PrivateApiLogin(); loggerConsole.Info("Modules"); - if (versionThisController >= version4_5) + if (versionThisController >= version20_4) + { + string modulesJSON = controllerApiParallel.GetDBModules_20_4(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); + if (modulesJSON != String.Empty) FileIOHelper.SaveFileToPath(modulesJSON, FilePathMap.DBModulesDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); + } + else if (versionThisController >= version4_5) { - string modulesJSON = controllerApiParallel.GetDBModules(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, "4.5"); + string modulesJSON = controllerApiParallel.GetDBModules_4_5(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); if (modulesJSON != String.Empty) FileIOHelper.SaveFileToPath(modulesJSON, FilePathMap.DBModulesDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); } else if (versionThisController >= version4_4) { - string modulesJSON = controllerApiParallel.GetDBModules(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, "4.4"); + string modulesJSON = controllerApiParallel.GetDBModules_4_4(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); if (modulesJSON != String.Empty) FileIOHelper.SaveFileToPath(modulesJSON, FilePathMap.DBModulesDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); } loggerConsole.Info("Programs"); - if (versionThisController >= version4_5) + if (versionThisController >= version20_4) { - string programsJSON = controllerApiParallel.GetDBPrograms(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, "4.5"); + string programsJSON = controllerApiParallel.GetDBPrograms_20_4(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); + if (programsJSON != String.Empty) FileIOHelper.SaveFileToPath(programsJSON, FilePathMap.DBProgramsDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); + } + else if (versionThisController >= version4_5) + { + string programsJSON = controllerApiParallel.GetDBPrograms_4_5(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); if (programsJSON != String.Empty) FileIOHelper.SaveFileToPath(programsJSON, FilePathMap.DBProgramsDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); } else if (versionThisController >= version4_4) { - string programsJSON = controllerApiParallel.GetDBPrograms(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, "4.4"); + string programsJSON = controllerApiParallel.GetDBPrograms_4_4(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); if (programsJSON != String.Empty) FileIOHelper.SaveFileToPath(programsJSON, FilePathMap.DBProgramsDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); } loggerConsole.Info("Business Transactions"); - string businessTransactionsJSON = controllerApiParallel.GetDBBusinessTransactions(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); - if (businessTransactionsJSON != String.Empty) FileIOHelper.SaveFileToPath(businessTransactionsJSON, FilePathMap.DBBusinessTransactionsDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); + if (versionThisController >= version20_4) + { + string businessTransactionsJSON = controllerApiParallel.GetDBBusinessTransactions_20_4(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); + if (businessTransactionsJSON != String.Empty) FileIOHelper.SaveFileToPath(businessTransactionsJSON, FilePathMap.DBBusinessTransactionsDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); + } + else + { + string businessTransactionsJSON = controllerApiParallel.GetDBBusinessTransactions_Pre_20_4(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix); + if (businessTransactionsJSON != String.Empty) FileIOHelper.SaveFileToPath(businessTransactionsJSON, FilePathMap.DBBusinessTransactionsDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); + } } }, () => @@ -263,17 +321,22 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job using (ControllerApi controllerApiParallel = new ControllerApi(jobTarget.Controller, jobTarget.UserName, AESEncryptionHelper.Decrypt(jobTarget.UserPassword))) { controllerApiParallel.PrivateApiLogin(); - + loggerConsole.Info("All Blocked Sessions"); string blockedSessionsJSON = String.Empty; - if (versionThisController >= version4_5) + if (versionThisController >= version20_4) { - blockedSessionsJSON = controllerApiParallel.GetDBBlockingSessions(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, differenceInMinutes, "4.5"); + blockedSessionsJSON = controllerApiParallel.GetDBBlockingSessions_20_4(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, differenceInMinutes); + if (blockedSessionsJSON != String.Empty) FileIOHelper.SaveFileToPath(blockedSessionsJSON, FilePathMap.DBBlockingSessionsDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); + } + else if (versionThisController >= version4_5) + { + blockedSessionsJSON = controllerApiParallel.GetDBBlockingSessions_4_5(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, differenceInMinutes); if (blockedSessionsJSON != String.Empty) FileIOHelper.SaveFileToPath(blockedSessionsJSON, FilePathMap.DBBlockingSessionsDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); } else if (versionThisController >= version4_4) { - blockedSessionsJSON = controllerApiParallel.GetDBBlockingSessions(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, differenceInMinutes, "4.4"); + blockedSessionsJSON = controllerApiParallel.GetDBBlockingSessions_4_4(jobTarget.DBCollectorID, fromTimeUnix, toTimeUnix, differenceInMinutes); if (blockedSessionsJSON != String.Empty) FileIOHelper.SaveFileToPath(blockedSessionsJSON, FilePathMap.DBBlockingSessionsDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); } @@ -290,14 +353,19 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job { long blockingSessionID = (long)blockedSessionToken["blockingSessionId"]; - if (versionThisController >= version4_5) + if (versionThisController >= version20_4) + { + string blockedSessionJSON = controllerApiParallel.GetDBBlockingSession_20_4(jobTarget.DBCollectorID, blockingSessionID, fromTimeUnix, toTimeUnix, differenceInMinutes); + if (blockedSessionJSON != String.Empty) FileIOHelper.SaveFileToPath(blockedSessionJSON, FilePathMap.DBBlockingSessionDataFilePath(jobTarget, jobConfiguration.Input.TimeRange, blockingSessionID)); + } + else if (versionThisController >= version4_5) { - string blockedSessionJSON = controllerApiParallel.GetDBBlockingSession(jobTarget.DBCollectorID, blockingSessionID, fromTimeUnix, toTimeUnix, differenceInMinutes, "4.5"); + string blockedSessionJSON = controllerApiParallel.GetDBBlockingSession_4_5(jobTarget.DBCollectorID, blockingSessionID, fromTimeUnix, toTimeUnix, differenceInMinutes); if (blockedSessionJSON != String.Empty) FileIOHelper.SaveFileToPath(blockedSessionJSON, FilePathMap.DBBlockingSessionDataFilePath(jobTarget, jobConfiguration.Input.TimeRange, blockingSessionID)); } else if (versionThisController >= version4_4) { - string blockedSessionJSON = controllerApiParallel.GetDBBlockingSession(jobTarget.DBCollectorID, blockingSessionID, fromTimeUnix, toTimeUnix, differenceInMinutes, "4.4"); + string blockedSessionJSON = controllerApiParallel.GetDBBlockingSession_4_4(jobTarget.DBCollectorID, blockingSessionID, fromTimeUnix, toTimeUnix, differenceInMinutes); if (blockedSessionJSON != String.Empty) FileIOHelper.SaveFileToPath(blockedSessionJSON, FilePathMap.DBBlockingSessionDataFilePath(jobTarget, jobConfiguration.Input.TimeRange, blockingSessionID)); } } diff --git a/ProcessingSteps/Extract/ExtractDashboards.cs b/ProcessingSteps/Extract/ExtractDashboards.cs index eba3bf1..878b40d 100644 --- a/ProcessingSteps/Extract/ExtractDashboards.cs +++ b/ProcessingSteps/Extract/ExtractDashboards.cs @@ -94,9 +94,19 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job var listOfDashboardsChunks = allDashboardsArray.BreakListIntoChunks(DASHBOARDS_EXTRACT_NUMBER_OF_ENTITIES_TO_PROCESS_PER_THREAD); + ParallelOptions parallelOptions = new ParallelOptions(); + if (programOptions.ProcessSequentially == true) + { + parallelOptions.MaxDegreeOfParallelism = 1; + } + else + { + parallelOptions.MaxDegreeOfParallelism = DASHBOARDS_EXTRACT_NUMBER_OF_THREADS; + } + Parallel.ForEach, int>( listOfDashboardsChunks, - new ParallelOptions { MaxDegreeOfParallelism = DASHBOARDS_EXTRACT_NUMBER_OF_THREADS }, + parallelOptions, () => 0, (listOfDashboardsChunk, loop, subtotal) => { diff --git a/ProcessingSteps/Extract/ExtractMOBILEConfiguration.cs b/ProcessingSteps/Extract/ExtractMOBILEConfiguration.cs index 96f87b5..60648ab 100644 --- a/ProcessingSteps/Extract/ExtractMOBILEConfiguration.cs +++ b/ProcessingSteps/Extract/ExtractMOBILEConfiguration.cs @@ -34,6 +34,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_MOBILE) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_MOBILE); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_MOBILE); + return true; } diff --git a/ProcessingSteps/Extract/ExtractMOBILEEntities.cs b/ProcessingSteps/Extract/ExtractMOBILEEntities.cs index ce178ab..5d2284d 100644 --- a/ProcessingSteps/Extract/ExtractMOBILEEntities.cs +++ b/ProcessingSteps/Extract/ExtractMOBILEEntities.cs @@ -43,6 +43,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_MOBILE) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_MOBILE); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_MOBILE); + return true; } @@ -109,9 +112,19 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job var listOfNetworkRequetsChunks = networkRequestsArray.BreakListIntoChunks(ENTITIES_EXTRACT_NUMBER_OF_PAGES_TO_PROCESS_PER_THREAD); + ParallelOptions parallelOptions = new ParallelOptions(); + if (programOptions.ProcessSequentially == true) + { + parallelOptions.MaxDegreeOfParallelism = 1; + } + else + { + parallelOptions.MaxDegreeOfParallelism = PAGES_EXTRACT_NUMBER_OF_THREADS; + } + Parallel.ForEach, int>( listOfNetworkRequetsChunks, - new ParallelOptions { MaxDegreeOfParallelism = PAGES_EXTRACT_NUMBER_OF_THREADS }, + parallelOptions, () => 0, (listOfNetworkRequetsChunk, loop, subtotal) => { diff --git a/ProcessingSteps/Extract/ExtractSIMEntities.cs b/ProcessingSteps/Extract/ExtractSIMEntities.cs index 909e1e6..68f7975 100644 --- a/ProcessingSteps/Extract/ExtractSIMEntities.cs +++ b/ProcessingSteps/Extract/ExtractSIMEntities.cs @@ -43,6 +43,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_SIM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_SIM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_SIM); + return true; } @@ -151,9 +154,19 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job var listOfMachinesChunks = machinesArray.BreakListIntoChunks(ENTITIES_EXTRACT_NUMBER_OF_MACHINES_TO_PROCESS_PER_THREAD); + ParallelOptions parallelOptions = new ParallelOptions(); + if (programOptions.ProcessSequentially == true) + { + parallelOptions.MaxDegreeOfParallelism = 1; + } + else + { + parallelOptions.MaxDegreeOfParallelism = MACHINES_EXTRACT_NUMBER_OF_THREADS; + } + Parallel.ForEach, int>( listOfMachinesChunks, - new ParallelOptions { MaxDegreeOfParallelism = MACHINES_EXTRACT_NUMBER_OF_THREADS }, + parallelOptions, () => 0, (listOfMachinesChunk, loop, subtotal) => { diff --git a/ProcessingSteps/Extract/ExtractWEBConfiguration.cs b/ProcessingSteps/Extract/ExtractWEBConfiguration.cs index 70ca2ed..ce47bc3 100644 --- a/ProcessingSteps/Extract/ExtractWEBConfiguration.cs +++ b/ProcessingSteps/Extract/ExtractWEBConfiguration.cs @@ -34,6 +34,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_WEB) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_WEB); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_WEB); + return true; } diff --git a/ProcessingSteps/Extract/ExtractWEBEntities.cs b/ProcessingSteps/Extract/ExtractWEBEntities.cs index 4adce57..8c3a497 100644 --- a/ProcessingSteps/Extract/ExtractWEBEntities.cs +++ b/ProcessingSteps/Extract/ExtractWEBEntities.cs @@ -42,6 +42,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_WEB) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_WEB); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_WEB); + return true; } @@ -108,9 +111,19 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job var listOfPagesChunks = pagesArray.BreakListIntoChunks(ENTITIES_EXTRACT_NUMBER_OF_PAGES_TO_PROCESS_PER_THREAD); + ParallelOptions parallelOptions = new ParallelOptions(); + if (programOptions.ProcessSequentially == true) + { + parallelOptions.MaxDegreeOfParallelism = 1; + } + else + { + parallelOptions.MaxDegreeOfParallelism = PAGES_EXTRACT_NUMBER_OF_THREADS; + } + Parallel.ForEach, int>( listOfPagesChunks, - new ParallelOptions { MaxDegreeOfParallelism = PAGES_EXTRACT_NUMBER_OF_THREADS }, + parallelOptions, () => 0, (listOfPagesChunk, loop, subtotal) => { diff --git a/ProcessingSteps/FilePathMap.cs b/ProcessingSteps/FilePathMap.cs index 2e00792..f925f0c 100644 --- a/ProcessingSteps/FilePathMap.cs +++ b/ProcessingSteps/FilePathMap.cs @@ -433,44 +433,45 @@ public class FilePathMap private const string REPORT_FOLDER_NAME = "Report"; // Report file names - private const string REPORT_DETECTED_APM_ENTITIES_FILE_NAME = "Entities.APM.{0}.{1:yyyyMMddHHmm}-{2:yyyyMMddHHmm}.xlsx"; - private const string REPORT_DETECTED_SIM_ENTITIES_FILE_NAME = "Entities.SIM.{0}.{1:yyyyMMddHHmm}-{2:yyyyMMddHHmm}.xlsx"; - private const string REPORT_DETECTED_DB_ENTITIES_FILE_NAME = "Entities.DB.{0}.{1:yyyyMMddHHmm}-{2:yyyyMMddHHmm}.xlsx"; - private const string REPORT_DETECTED_WEB_ENTITIES_FILE_NAME = "Entities.WEB.{0}.{1:yyyyMMddHHmm}-{2:yyyyMMddHHmm}.xlsx"; - private const string REPORT_DETECTED_MOBILE_ENTITIES_FILE_NAME = "Entities.MOBILE.{0}.{1:yyyyMMddHHmm}-{2:yyyyMMddHHmm}.xlsx"; - private const string REPORT_DETECTED_ANALYTICS_ENTITIES_FILE_NAME = "Entities.BIQ.{0}.{1:yyyyMMddHHmm}-{2:yyyyMMddHHmm}.xlsx"; - - private const string REPORT_METRICS_ALL_ENTITIES_FILE_NAME = "EntityMetrics.{0}.{1:yyyyMMddHHmm}-{2:yyyyMMddHHmm}.xlsx"; - private const string REPORT_DETECTED_EVENTS_FILE_NAME = "Events.{0}.{1:yyyyMMddHHmm}-{2:yyyyMMddHHmm}.xlsx"; - private const string REPORT_SNAPSHOTS_FILE_NAME = "Snapshots.{0}.{1:yyyyMMddHHmm}-{2:yyyyMMddHHmm}.xlsx"; - private const string REPORT_SNAPSHOTS_METHOD_CALL_LINES_FILE_NAME = "CallGraphs.{0}.{1:yyyyMMddHHmm}-{2:yyyyMMddHHmm}.xlsx"; - private const string REPORT_CONFIGURATION_FILE_NAME = "Configuration.{0}.{1:yyyyMMddHHmm}-{2:yyyyMMddHHmm}.xlsx"; - private const string REPORT_USERS_GROUPS_ROLES_PERMISSIONS_FILE_NAME = "UsersGroupsRoles.{0}.{1:yyyyMMddHHmm}-{2:yyyyMMddHHmm}.xlsx"; - private const string REPORT_DASHBOARDS_FILE_NAME = "Dashboards.{0}.{1:yyyyMMddHHmm}-{2:yyyyMMddHHmm}.xlsx"; - private const string REPORT_LICENSES_FILE_NAME = "Licenses.{0}.{1:yyyyMMddHHmm}-{2:yyyyMMddHHmm}.xlsx"; - private const string REPORT_APPLICATIONS_DASHBOARDS_FILE_NAME = "ApplicationsDashboards.{0}.{1:yyyyMMddHHmm}-{2:yyyyMMddHHmm}.html"; + private const string REPORT_DETECTED_APM_ENTITIES_FILE_NAME = "Entities.APM.{0}.xlsx"; + private const string REPORT_DETECTED_SIM_ENTITIES_FILE_NAME = "Entities.SIM.{0}.xlsx"; + private const string REPORT_DETECTED_DB_ENTITIES_FILE_NAME = "Entities.DB.{0}.xlsx"; + private const string REPORT_DETECTED_WEB_ENTITIES_FILE_NAME = "Entities.WEB.{0}.xlsx"; + private const string REPORT_DETECTED_MOBILE_ENTITIES_FILE_NAME = "Entities.MOBILE.{0}.xlsx"; + private const string REPORT_DETECTED_ANALYTICS_ENTITIES_FILE_NAME = "Entities.BIQ.{0}.xlsx"; + + private const string REPORT_METRICS_ALL_ENTITIES_FILE_NAME = "EntityMetrics.{0}.xlsx"; + private const string REPORT_FLOWMAPS_ALL_ENTITIES_FILE_NAME = "Flowmaps.{0}.xlsx"; + private const string REPORT_DETECTED_EVENTS_FILE_NAME = "Events.{0}.xlsx"; + private const string REPORT_SNAPSHOTS_FILE_NAME = "Snapshots.{0}.xlsx"; + private const string REPORT_SNAPSHOTS_METHOD_CALL_LINES_FILE_NAME = "CallGraphs.{0}.xlsx"; + private const string REPORT_CONFIGURATION_FILE_NAME = "Configuration.{0}.xlsx"; + private const string REPORT_USERS_GROUPS_ROLES_PERMISSIONS_FILE_NAME = "UsersGroupsRoles.{0}.xlsx"; + private const string REPORT_DASHBOARDS_FILE_NAME = "Dashboards.{0}.xlsx"; + private const string REPORT_LICENSES_FILE_NAME = "Licenses.{0}.xlsx"; + private const string REPORT_APPLICATIONS_DASHBOARDS_FILE_NAME = "ApplicationsDashboards.{0}.html"; private const string REPORT_APPLICATION_DASHBOARDS_FILE_NAME = "ApplicationDashboards.html"; - private const string REPORT_HEALTH_CHECK_RESULTS_FILE_NAME = "HealthCheck.{0}.{1:yyyyMMddHHmm}-{2:yyyyMMddHHmm}.xlsx"; + private const string REPORT_HEALTH_CHECK_RESULTS_FILE_NAME = "HealthCheck.{0}.xlsx"; // Per entity report names - private const string REPORT_ENTITY_DETAILS_APPLICATION_FILE_NAME = "EntityDetails.{0}.{1}.{2:yyyyMMddHHmm}-{3:yyyyMMddHHmm}.xlsx"; - private const string REPORT_ENTITY_DETAILS_ENTITY_FILE_NAME = "EntityDetails.{0}.{1}.{2}.{3:yyyyMMddHHmm}-{4:yyyyMMddHHmm}.xlsx"; - private const string REPORT_METRICS_GRAPHS_FILE_NAME = "MetricGraphs.{0}.{1}.{2}.{3:yyyyMMddHHmm}-{4:yyyyMMddHHmm}.xlsx"; + private const string REPORT_ENTITY_DETAILS_APPLICATION_FILE_NAME = "EntityDetails.{0}.{1}.xlsx"; + private const string REPORT_ENTITY_DETAILS_ENTITY_FILE_NAME = "EntityDetails.{0}.{1}.{2}.xlsx"; + private const string REPORT_METRICS_GRAPHS_FILE_NAME = "MetricGraphs.{0}.{1}.{2}.xlsx"; // Per entity flame graph report name - private const string REPORT_FLAME_GRAPH_APPLICATION_FILE_NAME = "FlameGraph.Application.{0}.{1}.{2:yyyyMMddHHmm}-{3:yyyyMMddHHmm}.svg"; - private const string REPORT_FLAME_GRAPH_TIER_FILE_NAME = "FlameGraph.Tier.{0}.{1}.{2}.{3:yyyyMMddHHmm}-{4:yyyyMMddHHmm}.svg"; - private const string REPORT_FLAME_GRAPH_NODE_FILE_NAME = "FlameGraph.Node.{0}.{1}.{2}.{3:yyyyMMddHHmm}-{4:yyyyMMddHHmm}.svg"; - private const string REPORT_FLAME_GRAPH_BUSINESS_TRANSACTION_FILE_NAME = "FlameGraph.BT.{0}.{1}.{2}.{3:yyyyMMddHHmm}-{4:yyyyMMddHHmm}.svg"; + private const string REPORT_FLAME_GRAPH_APPLICATION_FILE_NAME = "FlameGraph.Application.{0}.{1}.svg"; + private const string REPORT_FLAME_GRAPH_TIER_FILE_NAME = "FlameGraph.Tier.{0}.{1}.{2}.svg"; + private const string REPORT_FLAME_GRAPH_NODE_FILE_NAME = "FlameGraph.Node.{0}.{1}.{2}.svg"; + private const string REPORT_FLAME_GRAPH_BUSINESS_TRANSACTION_FILE_NAME = "FlameGraph.BT.{0}.{1}.{2}.svg"; // Per entity flame chart report name - private const string REPORT_FLAME_CHART_APPLICATION_FILE_NAME = "FlameChart.Application.{0}.{1}.{2:yyyyMMddHHmm}-{3:yyyyMMddHHmm}.svg"; - private const string REPORT_FLAME_CHART_TIER_FILE_NAME = "FlameChart.Tier.{0}.{1}.{2}.{3:yyyyMMddHHmm}-{4:yyyyMMddHHmm}.svg"; - private const string REPORT_FLAME_CHART_NODE_FILE_NAME = "FlameChart.Node.{0}.{1}.{2}.{3:yyyyMMddHHmm}-{4:yyyyMMddHHmm}.svg"; - private const string REPORT_FLAME_CHART_BUSINESS_TRANSACTION_FILE_NAME = "FlameChart.BT.{0}.{1}.{2}.{3:yyyyMMddHHmm}-{4:yyyyMMddHHmm}.svg"; + private const string REPORT_FLAME_CHART_APPLICATION_FILE_NAME = "FlameChart.Application.{0}.{1}.svg"; + private const string REPORT_FLAME_CHART_TIER_FILE_NAME = "FlameChart.Tier.{0}.{1}.{2}.svg"; + private const string REPORT_FLAME_CHART_NODE_FILE_NAME = "FlameChart.Node.{0}.{1}.{2}.svg"; + private const string REPORT_FLAME_CHART_BUSINESS_TRANSACTION_FILE_NAME = "FlameChart.BT.{0}.{1}.{2}.svg"; // Per Application - private const string REPORT_APPLICATION_SUMMARY_FILE_NAME = "ApplicationSummary.{0}.{1}.{2:yyyyMMddHHmm}-{3:yyyyMMddHHmm}.docx"; + private const string REPORT_APPLICATION_SUMMARY_FILE_NAME = "ApplicationSummary.{0}.{1}.docx"; #endregion @@ -5356,6 +5357,19 @@ public string BusinessTransactionsFlowmapReportFilePath() reportFileName); } + public string FlowmapsExcelReportFilePath(JobTimeRange jobTimeRange) + { + string reportFileName = String.Format( + REPORT_FLOWMAPS_ALL_ENTITIES_FILE_NAME, + this.ProgramOptions.JobName, + jobTimeRange.From, + jobTimeRange.To); + return Path.Combine( + this.ProgramOptions.OutputJobFolderPath, + REPORT_FOLDER_NAME, + reportFileName); + } + #endregion diff --git a/ProcessingSteps/Index/IndexAPMConfiguration.cs b/ProcessingSteps/Index/IndexAPMConfiguration.cs index 3ce3f44..da81fbd 100644 --- a/ProcessingSteps/Index/IndexAPMConfiguration.cs +++ b/ProcessingSteps/Index/IndexAPMConfiguration.cs @@ -41,6 +41,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_APM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + return true; } diff --git a/ProcessingSteps/Index/IndexAPMEntities.cs b/ProcessingSteps/Index/IndexAPMEntities.cs index ed15660..0131481 100644 --- a/ProcessingSteps/Index/IndexAPMEntities.cs +++ b/ProcessingSteps/Index/IndexAPMEntities.cs @@ -40,6 +40,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_APM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + return true; } diff --git a/ProcessingSteps/Index/IndexAPMFlowmaps.cs b/ProcessingSteps/Index/IndexAPMFlowmaps.cs index 5329551..33c30e5 100644 --- a/ProcessingSteps/Index/IndexAPMFlowmaps.cs +++ b/ProcessingSteps/Index/IndexAPMFlowmaps.cs @@ -38,6 +38,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_APM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + return true; } @@ -81,7 +84,13 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job tiersAllIndexedItemsDictionary = new Dictionary(); } - Parallel.Invoke( + ParallelOptions parallelOptions = new ParallelOptions(); + if (programOptions.ProcessSequentially == true) + { + parallelOptions.MaxDegreeOfParallelism = 1; + } + + Parallel.Invoke(parallelOptions, () => { #region Application @@ -499,10 +508,12 @@ private List convertFlowmapApplication(APMApplication application, deepLinkMetricTemplateInMetricBrowser = DEEPLINK_METRIC_TIER_TARGET_METRIC_ID; activityFlowTemplate.FromType = APMApplication.ENTITY_TYPE; activityFlowTemplate.FromLink = String.Format(DEEPLINK_APM_APPLICATION, activityFlowTemplate.Controller, activityFlowTemplate.FromEntityID, DEEPLINK_THIS_TIMERANGE); + activityFlowTemplate.IsCrossApplication = true; break; case ENTITY_TYPE_FLOWMAP_FEDERATED_APPLICATION: activityFlowTemplate.FromType = "Federated Application"; + activityFlowTemplate.IsCrossApplication = true; break; default: @@ -531,13 +542,15 @@ private List convertFlowmapApplication(APMApplication application, case ENTITY_TYPE_FLOWMAP_APPLICATION: // Let's build a pretty call chain that can be matched on the downstream application flowmap - activityFlowTemplate.ToName = String.Format("{{{0}}}->({1})-{{{2}}}", activityFlowTemplate.ApplicationName, activityFlowTemplate.FromName, activityFlowTemplate.ToName); + activityFlowTemplate.ToName = String.Format("{{{0}}}->({1})->{{{2}}}", activityFlowTemplate.ApplicationName, activityFlowTemplate.FromName, activityFlowTemplate.ToName); activityFlowTemplate.ToType = APMApplication.ENTITY_TYPE; activityFlowTemplate.ToLink = String.Format(DEEPLINK_APM_APPLICATION, activityFlowTemplate.Controller, activityFlowTemplate.ToEntityID, DEEPLINK_THIS_TIMERANGE); + activityFlowTemplate.IsCrossApplication = true; break; case ENTITY_TYPE_FLOWMAP_FEDERATED_APPLICATION: activityFlowTemplate.ToType = "Federated Application"; + activityFlowTemplate.IsCrossApplication = true; break; default: @@ -560,7 +573,8 @@ private List convertFlowmapApplication(APMApplication application, APMTier tierLookup = null; if (tiersDictionary.TryGetValue(String.Format("{0}/{1}", activityFlowTemplate.FromEntityID, getLongValueFromJToken(entityConnectionStat["upstreamCrossAppCallingEntity"], "entityId")), out tierLookup) == true) { - activityFlow.FromName = String.Format("{{{0}}}->({1})-{{{2}}}", activityFlow.FromName, tierLookup.TierName, activityFlow.ApplicationName); + activityFlow.FromName = String.Format("{{{0}}}->({1})->{{{2}}}", activityFlow.FromName, tierLookup.TierName, activityFlow.ApplicationName); + activityFlow.IsCrossApplication = true; } } } @@ -705,10 +719,12 @@ private List convertFlowmapTier(APMTier tier, Dictionary convertFlowmapTier(APMTier tier, Dictionary({1})-{{{2}}}", activityFlowTemplate.ApplicationName, activityFlowTemplate.FromName, activityFlowTemplate.ToName); + activityFlowTemplate.ToName = String.Format("{{{0}}}->({1})->{{{2}}}", activityFlowTemplate.ApplicationName, activityFlowTemplate.FromName, activityFlowTemplate.ToName); activityFlowTemplate.ToType = APMApplication.ENTITY_TYPE; activityFlowTemplate.ToLink = String.Format(DEEPLINK_APM_APPLICATION, activityFlowTemplate.Controller, activityFlowTemplate.ToEntityID, DEEPLINK_THIS_TIMERANGE); + activityFlowTemplate.IsCrossApplication = true; break; case ENTITY_TYPE_FLOWMAP_FEDERATED_APPLICATION: activityFlowTemplate.ToType = "Federated Application"; + activityFlowTemplate.IsCrossApplication = true; break; default: @@ -775,7 +793,8 @@ private List convertFlowmapTier(APMTier tier, Dictionary({1})-{{{2}}}", activityFlow.FromName, tierLookup.TierName, activityFlow.ApplicationName); + activityFlow.FromName = String.Format("{{{0}}}->({1})->{{{2}}}", activityFlow.FromName, tierLookup.TierName, activityFlow.ApplicationName); + activityFlow.IsCrossApplication = true; } } } @@ -930,10 +949,12 @@ private List convertFlowmapNode(APMNode node, Dictionary convertFlowmapNode(APMNode node, Dictionary({1})-{{{2}}}", activityFlowTemplate.ApplicationName, activityFlowTemplate.TierName, activityFlowTemplate.ToName); + activityFlowTemplate.ToName = String.Format("{{{0}}}->({1})->{{{2}}}", activityFlowTemplate.ApplicationName, activityFlowTemplate.TierName, activityFlowTemplate.ToName); activityFlowTemplate.ToType = APMApplication.ENTITY_TYPE; activityFlowTemplate.ToLink = String.Format(DEEPLINK_APM_APPLICATION, activityFlowTemplate.Controller, activityFlowTemplate.ToEntityID, DEEPLINK_THIS_TIMERANGE); + activityFlowTemplate.IsCrossApplication = true; break; case ENTITY_TYPE_FLOWMAP_FEDERATED_APPLICATION: activityFlowTemplate.ToType = "Federated Application"; + activityFlowTemplate.IsCrossApplication = true; break; default: @@ -1006,7 +1029,8 @@ private List convertFlowmapNode(APMNode node, Dictionary({1})-{{{2}}}", activityFlow.FromName, tierLookup.TierName, activityFlow.ApplicationName); + activityFlow.FromName = String.Format("{{{0}}}->({1})->{{{2}}}", activityFlow.FromName, tierLookup.TierName, activityFlow.ApplicationName); + activityFlow.IsCrossApplication = true; } } } @@ -1152,6 +1176,7 @@ private List convertFlowmapBackend(APMBackend backend, JobTarget j deepLinkMetricTemplateInMetricBrowser = DEEPLINK_METRIC_TIER_TARGET_METRIC_ID; activityFlowTemplate.FromType = APMApplication.ENTITY_TYPE; activityFlowTemplate.FromLink = String.Format(DEEPLINK_APM_APPLICATION, activityFlowTemplate.Controller, activityFlowTemplate.FromEntityID, DEEPLINK_THIS_TIMERANGE); + activityFlowTemplate.IsCrossApplication = true; break; default: @@ -1181,6 +1206,7 @@ private List convertFlowmapBackend(APMBackend backend, JobTarget j case ENTITY_TYPE_FLOWMAP_APPLICATION: activityFlowTemplate.ToType = APMApplication.ENTITY_TYPE; activityFlowTemplate.ToLink = String.Format(DEEPLINK_APM_APPLICATION, activityFlowTemplate.Controller, activityFlowTemplate.ToEntityID, DEEPLINK_THIS_TIMERANGE); + activityFlowTemplate.IsCrossApplication = true; break; default: @@ -1351,10 +1377,12 @@ private List convertFlowmapsBusinessTransaction(APMBusinessTransac deepLinkMetricTemplateInMetricBrowser = DEEPLINK_METRIC_TIER_TARGET_METRIC_ID; activityFlowTemplate.FromType = APMApplication.ENTITY_TYPE; activityFlowTemplate.FromLink = String.Format(DEEPLINK_APM_APPLICATION, activityFlowTemplate.Controller, activityFlowTemplate.FromEntityID, DEEPLINK_THIS_TIMERANGE); + activityFlowTemplate.IsCrossApplication = true; break; case ENTITY_TYPE_FLOWMAP_FEDERATED_APPLICATION: activityFlowTemplate.FromType = "Federated Application"; + activityFlowTemplate.IsCrossApplication = true; break; default: @@ -1383,13 +1411,15 @@ private List convertFlowmapsBusinessTransaction(APMBusinessTransac case ENTITY_TYPE_FLOWMAP_APPLICATION: // Let's build a pretty call chain that can be matched on the downstream application flowmap - activityFlowTemplate.ToName = String.Format("{{{0}}}->({1})-{{{2}}}", activityFlowTemplate.ApplicationName, activityFlowTemplate.FromName, activityFlowTemplate.ToName); + activityFlowTemplate.ToName = String.Format("{{{0}}}->({1})->{{{2}}}", activityFlowTemplate.ApplicationName, activityFlowTemplate.FromName, activityFlowTemplate.ToName); activityFlowTemplate.ToType = APMApplication.ENTITY_TYPE; activityFlowTemplate.ToLink = String.Format(DEEPLINK_APM_APPLICATION, activityFlowTemplate.Controller, activityFlowTemplate.ToEntityID, DEEPLINK_THIS_TIMERANGE); + activityFlowTemplate.IsCrossApplication = true; break; case ENTITY_TYPE_FLOWMAP_FEDERATED_APPLICATION: activityFlowTemplate.ToType = "Federated Application"; + activityFlowTemplate.IsCrossApplication = true; break; default: @@ -1423,7 +1453,8 @@ private List convertFlowmapsBusinessTransaction(APMBusinessTransac APMTier tierLookup = null; if (tiersDictionary.TryGetValue(String.Format("{0}/{1}", activityFlowTemplate.FromEntityID, getLongValueFromJToken(entityConnectionStat["upstreamCrossAppCallingEntity"], "entityId")), out tierLookup) == true) { - activityFlow.FromName = String.Format("{{{0}}}->({1})-{{{2}}}", activityFlow.FromName, tierLookup.TierName, activityFlow.ApplicationName); + activityFlow.FromName = String.Format("{{{0}}}->({1})->{{{2}}}", activityFlow.FromName, tierLookup.TierName, activityFlow.ApplicationName); + activityFlow.IsCrossApplication = true; } } } diff --git a/ProcessingSteps/Index/IndexAPMHealthCheck.cs b/ProcessingSteps/Index/IndexAPMHealthCheck.cs index 2f63d94..4612cea 100644 --- a/ProcessingSteps/Index/IndexAPMHealthCheck.cs +++ b/ProcessingSteps/Index/IndexAPMHealthCheck.cs @@ -38,6 +38,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_APM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + return true; } @@ -484,7 +487,7 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job healthCheckRuleResults.AddRange( evaluate_APMNode_AgentVersion_APM( - new HealthCheckRuleDescription("APM Node Agent Version", "APM-080-NODE-VERSION-APM", "Node Agent Version"), + new HealthCheckRuleDescription("APM Node Agent Version", "APM-085-NODE-VERSION-APM", "Node Agent Version"), jobTarget, healthCheckSettingsDictionary, jobConfiguration, @@ -492,7 +495,7 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job healthCheckRuleResults.AddRange( evaluate_APMNode_AgentVersion_Machine( - new HealthCheckRuleDescription("APM Node Agent Version", "APM-081-NODE-VERSION-MACHINE", "Node Machine Agent Version"), + new HealthCheckRuleDescription("APM Node Agent Version", "APM-086-NODE-VERSION-MACHINE", "Node Machine Agent Version"), jobTarget, healthCheckSettingsDictionary, jobConfiguration, @@ -2847,7 +2850,7 @@ private List evaluate_APMNode_AgentVersion_APM( } else { - healthCheckRuleResult1.Description = String.Format("Machine", node.TierName, node.NodeName, node.AgentType, node.AgentVersion); + healthCheckRuleResult1.Description = String.Format("The Agent '{0}\\{1} [{2}]' is version '{3}'", node.TierName, node.NodeName, node.AgentType, node.AgentVersion); Version nodeVersion = new Version(node.AgentVersion); @@ -2904,7 +2907,7 @@ private List evaluate_APMNode_AgentVersion_Machine( if (node.MachineAgentPresent == false || node.MachineAgentVersion.Length == 0) { healthCheckRuleResult1.Grade = 1; - healthCheckRuleResult1.Description = String.Format("The Node '{0}\\{1} [{2}]' does not have a Machine Agent deployed", node.TierName, node.NodeName, node.AgentType, node.AgentVersion); + healthCheckRuleResult1.Description = String.Format("The Node '{0}\\{1} [{2}]' does not have a Machine Agent deployed", node.TierName, node.NodeName, node.AgentType); } else { diff --git a/ProcessingSteps/Index/IndexAPMMetrics.cs b/ProcessingSteps/Index/IndexAPMMetrics.cs index c6bc62a..d658814 100644 --- a/ProcessingSteps/Index/IndexAPMMetrics.cs +++ b/ProcessingSteps/Index/IndexAPMMetrics.cs @@ -38,6 +38,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_APM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + return true; } @@ -70,7 +73,13 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job int numEntitiesTotal = 0; - Parallel.Invoke( + ParallelOptions parallelOptions = new ParallelOptions(); + if (programOptions.ProcessSequentially == true) + { + parallelOptions.MaxDegreeOfParallelism = 1; + } + + Parallel.Invoke(parallelOptions, () => { #region Application diff --git a/ProcessingSteps/Index/IndexAPMSnapshots.cs b/ProcessingSteps/Index/IndexAPMSnapshots.cs index 3cf9ffa..8cb4c09 100644 --- a/ProcessingSteps/Index/IndexAPMSnapshots.cs +++ b/ProcessingSteps/Index/IndexAPMSnapshots.cs @@ -45,6 +45,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_APM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + return true; } @@ -189,10 +192,18 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job // Prepare thread safe storage to dump all those chunks ConcurrentBag indexedSnapshotsResultsBag = new ConcurrentBag(); + ParallelOptions parallelOptions = new ParallelOptions(); + if (programOptions.ProcessSequentially == true) + { + parallelOptions.MaxDegreeOfParallelism = 1; + } + int k = 0; + // Index them in parallel Parallel.ForEach, IndexedSnapshotsResults>( listOfSnapshotsInHourChunks, + parallelOptions, () => new IndexedSnapshotsResults(chunkSize), (listOfSnapshotsInHourChunk, loop, subtotal) => { @@ -2887,25 +2898,25 @@ private List convertCallGraphChildren_Stack( if (methodCallLine.Type == "JS") { // Node.js call graphs should use pretty name - if (methodCallLine.LineNumber > 0) - { - methodCallLine.FullName = String.Format("{0}:{1}", methodCallLine.PrettyName, methodCallLine.LineNumber); - } - else - { + //if (methodCallLine.LineNumber > 0) + //{ + // methodCallLine.FullName = String.Format("{0}:{1}", methodCallLine.PrettyName, methodCallLine.LineNumber); + //} + //else + //{ methodCallLine.FullName = methodCallLine.PrettyName; - } + //} } else { - if (methodCallLine.LineNumber > 0) - { - methodCallLine.FullName = String.Format("{0}:{1}:{2}", methodCallLine.Class, methodCallLine.Method, methodCallLine.LineNumber); - } - else - { + //if (methodCallLine.LineNumber > 0) + //{ + // methodCallLine.FullName = String.Format("{0}:{1}:{2}", methodCallLine.Class, methodCallLine.Method, methodCallLine.LineNumber); + //} + //else + //{ methodCallLine.FullName = String.Format("{0}:{1}", methodCallLine.Class, methodCallLine.Method); - } + //} } // Specify depth diff --git a/ProcessingSteps/Index/IndexBIQConfiguration.cs b/ProcessingSteps/Index/IndexBIQConfiguration.cs index 9d1782d..d7cf2e7 100644 --- a/ProcessingSteps/Index/IndexBIQConfiguration.cs +++ b/ProcessingSteps/Index/IndexBIQConfiguration.cs @@ -34,6 +34,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_BIQ) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_BIQ); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_BIQ); + return true; } diff --git a/ProcessingSteps/Index/IndexBIQEntities.cs b/ProcessingSteps/Index/IndexBIQEntities.cs index 27d4d9b..ab2f3ee 100644 --- a/ProcessingSteps/Index/IndexBIQEntities.cs +++ b/ProcessingSteps/Index/IndexBIQEntities.cs @@ -39,6 +39,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_BIQ) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_BIQ); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_BIQ); + return true; } diff --git a/ProcessingSteps/Index/IndexDBEntities.cs b/ProcessingSteps/Index/IndexDBEntities.cs index 36ae031..c066799 100644 --- a/ProcessingSteps/Index/IndexDBEntities.cs +++ b/ProcessingSteps/Index/IndexDBEntities.cs @@ -37,6 +37,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_DB) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_DB); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_DB); + return true; } @@ -75,7 +78,7 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job { JObject dbCollectorsCallsContainerObject = FileIOHelper.LoadJObjectFromFile(FilePathMap.DBCollectorsCallsDataFilePath(jobTarget)); JObject dbCollectorsTimeSpentContainerObject = FileIOHelper.LoadJObjectFromFile(FilePathMap.DBCollectorsTimeSpentDataFilePath(jobTarget)); - JArray dbCollectorDefinitionsArray = FileIOHelper.LoadJArrayFromFile(FilePathMap.DBCollectorDefinitionsDataFilePath(jobTarget)); + JArray dbCollectorDefinitionsArray = FileIOHelper.LoadJArrayFromFile(FilePathMap.DBCollectorDefinitionsForEntitiesFilePath(jobTarget)); JArray dbCollectorsCallsArray = null; JArray dbCollectorsTimeSpentArray = null; @@ -105,11 +108,11 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (dbCollectorDefinitionsArray != null) { - // Find the Collector Definition for this Collector - JToken dbCollectorDefinitionToken = dbCollectorDefinitionsArray.Where(r => getLongValueFromJToken(r, "configId") == getLongValueFromJToken(dbCollectorToken, "configId")).FirstOrDefault(); - if (isTokenNull(dbCollectorDefinitionToken) == false) + try { - if (isTokenPropertyNull(dbCollectorDefinitionToken, "config") == false) + // Find the Collector Definition for this Collector + JToken dbCollectorDefinitionToken = dbCollectorDefinitionsArray.Where(r => getLongValueFromJToken(r["config"], "id") == getLongValueFromJToken(dbCollectorToken, "configId")).FirstOrDefault(); + if (isTokenNull(dbCollectorDefinitionToken) == false) { JToken dbCollectorDefinitionConfigToken = dbCollectorDefinitionToken["config"]; @@ -121,6 +124,7 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job dbCollector.CollectorStatus = getStringValueFromJToken(dbCollectorDefinitionToken, "collectorStatus"); } } + catch { } } // Performance data @@ -204,7 +208,11 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job #region Wait States // Find the Collector Definition for this Collector - DBCollector dbCollectorThis = dbCollectorsList.Where(d => d.CollectorID == jobTarget.DBCollectorID).FirstOrDefault(); + DBCollector dbCollectorThis = null; + if (dbCollectorsList != null) + { + dbCollectorThis = dbCollectorsList.Where(d => d.CollectorID == jobTarget.DBCollectorID).FirstOrDefault(); + } JArray allDBWaitStatesArray = FileIOHelper.LoadJArrayFromFile(FilePathMap.DBAllWaitStatesDataFilePath(jobTarget)); JObject currentDBWaitStatesContainerObject = FileIOHelper.LoadJObjectFromFile(FilePathMap.DBCurrentWaitStatesDataFilePath(jobTarget, jobConfiguration.Input.TimeRange)); diff --git a/ProcessingSteps/Index/IndexMOBILEConfiguration.cs b/ProcessingSteps/Index/IndexMOBILEConfiguration.cs index a7ae49c..deb1be6 100644 --- a/ProcessingSteps/Index/IndexMOBILEConfiguration.cs +++ b/ProcessingSteps/Index/IndexMOBILEConfiguration.cs @@ -38,6 +38,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_MOBILE) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_MOBILE); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_MOBILE); + return true; } diff --git a/ProcessingSteps/Index/IndexMOBILEEntities.cs b/ProcessingSteps/Index/IndexMOBILEEntities.cs index be48f8a..c1c8a1a 100644 --- a/ProcessingSteps/Index/IndexMOBILEEntities.cs +++ b/ProcessingSteps/Index/IndexMOBILEEntities.cs @@ -38,6 +38,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_MOBILE) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_MOBILE); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_MOBILE); + return true; } diff --git a/ProcessingSteps/Index/IndexSIMEntities.cs b/ProcessingSteps/Index/IndexSIMEntities.cs index da45fe4..1da8438 100644 --- a/ProcessingSteps/Index/IndexSIMEntities.cs +++ b/ProcessingSteps/Index/IndexSIMEntities.cs @@ -38,6 +38,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_SIM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_SIM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_SIM); + return true; } diff --git a/ProcessingSteps/Index/IndexWEBConfiguration.cs b/ProcessingSteps/Index/IndexWEBConfiguration.cs index 7d99d82..9241f95 100644 --- a/ProcessingSteps/Index/IndexWEBConfiguration.cs +++ b/ProcessingSteps/Index/IndexWEBConfiguration.cs @@ -38,6 +38,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_WEB) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_WEB); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_WEB); + return true; } diff --git a/ProcessingSteps/Index/IndexWEBEntities.cs b/ProcessingSteps/Index/IndexWEBEntities.cs index aba3e9c..11ee49c 100644 --- a/ProcessingSteps/Index/IndexWEBEntities.cs +++ b/ProcessingSteps/Index/IndexWEBEntities.cs @@ -38,6 +38,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_WEB) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_WEB); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_WEB); + return true; } diff --git a/ProcessingSteps/JobStepBase.cs b/ProcessingSteps/JobStepBase.cs index c15067e..6a26629 100644 --- a/ProcessingSteps/JobStepBase.cs +++ b/ProcessingSteps/JobStepBase.cs @@ -119,6 +119,7 @@ public class JobStepBase // https://docs.appdynamics.com/display/PRO43/Build+a+Custom+Action // They are defined here : // C:\appdynamics\codebase\controller\controller-api\agent\src\main\java\com\singularity\ee\controller\api\constants\EventType.java + // https://bitbucket.corp.appdynamics.com/projects/CON/repos/controller/raw/controller/controller-api/agent/src/main/java/com/singularity/ee/controller/api/constants/EventType.java // But filtering that to only the ones that aren't deprecated internal static List EVENT_TYPES = new List { @@ -238,6 +239,19 @@ public class JobStepBase { "MEMORY_LEAK_DIAGNOSTICS" }, { "OBJECT_CONTENT_SUMMARY" }, + // Anomaly Detection + { "ANOMALY_OPEN_CRITICAL" }, + { "ANOMALY_OPEN_WARNING" }, + { "ANOMALY_UPGRADED" }, + { "ANOMALY_DOWNGRADED" }, + { "ANOMALY_CLOSE_CRITICAL" }, + { "ANOMALY_CLOSE_WARNING" }, + { "ANOMALY_CANCELED_CRITICAL" }, + { "ANOMALY_CANCELED_WARNING" }, + + // Cluster Agent + { "KUBERNETES" }, + // Others { "CONTROLLER_AGENT_VERSION_INCOMPATIBILITY" }, { "LICENSE" }, diff --git a/ProcessingSteps/JobStepRouter.cs b/ProcessingSteps/JobStepRouter.cs index 78c8c57..a42bedb 100644 --- a/ProcessingSteps/JobStepRouter.cs +++ b/ProcessingSteps/JobStepRouter.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.Reflection; -using System.Text; namespace AppDynamics.Dexter { @@ -96,6 +95,7 @@ public class JobStepRouter JobStatus.ReportAPMMetrics, JobStatus.ReportAPMMetricGraphs, + JobStatus.ReportAPMFlowmaps, JobStatus.ReportAPMSnapshots, JobStatus.ReportAPMSnapshotsMethodCallLines, @@ -122,7 +122,7 @@ public static void ExecuteJobThroughSteps(ProgramOptions programOptions) JobConfiguration jobConfiguration = FileIOHelper.ReadJobConfigurationFromFile(programOptions.OutputJobFilePath); if (jobConfiguration == null) { - loggerConsole.Error("Unable to load job input file {0}", programOptions.InputJobFilePath); + loggerConsole.Error("Unable to load job input file {0}", programOptions.InputETLJobFilePath); return; } @@ -148,25 +148,13 @@ public static void ExecuteJobThroughSteps(ProgramOptions programOptions) } if (jobConfiguration.Input.SnapshotSelectionCriteria != null) { - PropertyInfo[] pis = jobConfiguration.Input.SnapshotSelectionCriteria.TierType.GetType().GetProperties(); - StringBuilder sb = new StringBuilder(16 * pis.Length); - foreach (PropertyInfo pi in pis) - { - sb.AppendFormat("{0}={1}, ", pi.Name, pi.GetValue(jobConfiguration.Input.SnapshotSelectionCriteria.TierType)); - } + PropertyInfo[] pis = jobConfiguration.Input.SnapshotSelectionCriteria.TierTypes.GetType().GetProperties(); logger.Info("Job input, SnapshotSelectionCriteria: Tiers='{0}', TierTypes='{1}'", String.Join(",", jobConfiguration.Input.SnapshotSelectionCriteria.Tiers), - sb.ToString()); - - pis = jobConfiguration.Input.SnapshotSelectionCriteria.BusinessTransactionType.GetType().GetProperties(); - sb = new StringBuilder(16 * pis.Length); - foreach (PropertyInfo pi in pis) - { - sb.AppendFormat("{0}={1}, ", pi.Name, pi.GetValue(jobConfiguration.Input.SnapshotSelectionCriteria.BusinessTransactionType)); - } + String.Join(",", jobConfiguration.Input.SnapshotSelectionCriteria.TierTypes)); logger.Info("Job input, SnapshotSelectionCriteria: BusinessTransactions='{0}', BusinessTransactionType='{1}'", String.Join(",", jobConfiguration.Input.SnapshotSelectionCriteria.BusinessTransactions), - sb.ToString()); + String.Join(",", jobConfiguration.Input.SnapshotSelectionCriteria.BusinessTransactionTypes)); logger.Info("Job input, SnapshotSelectionCriteria: UserExperience.Normal='{0}', UserExperience.Slow='{1}', UserExperience.VerySlow='{2}', UserExperience.Stall='{3}', UserExperience.Error='{4}'", jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience.Normal, jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience.Slow, @@ -183,13 +171,25 @@ public static void ExecuteJobThroughSteps(ProgramOptions programOptions) logger.Info("Job input: ConfigurationComparisonReferenceMOBILE='{0}'", jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE); logger.Info("Job input: ConfigurationComparisonReferenceDB='{0}'", jobConfiguration.Input.ConfigurationComparisonReferenceDB); - logger.Info("Job output: DetectedEntities='{0}', EntityMetrics='{1}', EntityDetails='{2}', Snapshots='{3}', Configuration='{4}', Events='{5}'", jobConfiguration.Output.DetectedEntities, jobConfiguration.Output.EntityMetrics, jobConfiguration.Output.EntityDetails, jobConfiguration.Output.Snapshots, jobConfiguration.Output.Configuration, jobConfiguration.Output.Events); - foreach (JobTimeRange jobTimeRange in jobConfiguration.Input.HourlyTimeRanges) { logger.Info("Expanded time ranges: From='{0:o}', To='{1:o}'", jobTimeRange.From, jobTimeRange.To); } + logger.Info("Job output:"); + logger.Info("DetectedEntities='{0}'", jobConfiguration.Output.DetectedEntities); + logger.Info("Flowmaps='{0}'", jobConfiguration.Output.Flowmaps); + logger.Info("EntityMetrics='{0}'", jobConfiguration.Output.EntityMetrics); + logger.Info("EntityMetricsGraphs='{0}'", jobConfiguration.Output.EntityMetricGraphs); + logger.Info("Snapshots='{0}'", jobConfiguration.Output.Snapshots); + logger.Info("Events='{0}'", jobConfiguration.Output.Events); + logger.Info("Licenses='{0}''", jobConfiguration.Output.Licenses); + logger.Info("Configuration='{0}'", jobConfiguration.Output.Configuration); + logger.Info("UsersGroupsRolesPermissions='{0}'", jobConfiguration.Output.UsersGroupsRolesPermissions); + logger.Info("EntityDashboards='{0}'", jobConfiguration.Output.EntityDashboards); + logger.Info("HealthCheck='{0}'", jobConfiguration.Output.HealthCheck); + logger.Info("ApplicationSummary='{0}'", jobConfiguration.Output.ApplicationSummary); + #endregion // Run the step and move to next until things are done @@ -220,10 +220,6 @@ public static void ExecuteJobThroughSteps(ProgramOptions programOptions) return; } - - // Do a forced GC Collection after all the work in a step. - // Probably unnecessary, but to counteract the ulimit handle consumption on non-Windows machines - GC.Collect(); } } @@ -375,6 +371,8 @@ private static JobStepBase getJobStepFromFactory(JobStatus jobStatus) return new ReportAPMMetrics(); case JobStatus.ReportAPMMetricGraphs: return new ReportAPMMetricGraphs(); + case JobStatus.ReportAPMFlowmaps: + return new ReportAPMFlowmaps(); case JobStatus.ReportAPMSnapshots: return new ReportAPMSnapshots(); diff --git a/ProcessingSteps/Report/JobStepReportBase.cs b/ProcessingSteps/Report/JobStepReportBase.cs index 7080c2a..b3fd59e 100644 --- a/ProcessingSteps/Report/JobStepReportBase.cs +++ b/ProcessingSteps/Report/JobStepReportBase.cs @@ -209,6 +209,9 @@ internal static void fillReportParametersSheet(ExcelWorksheet sheet, JobConfigur l++; sheet.Cells[l, 1].Value = "Export Dashboards"; sheet.Cells[l, 2].Value = jobConfiguration.Input.Dashboards; + l++; + sheet.Cells[l, 1].Value = "Export Flowmaps"; + sheet.Cells[l, 2].Value = jobConfiguration.Input.Flowmaps; l++; l++; sheet.Cells[l, 1].Value = "Targets:"; l++; l++; diff --git a/ProcessingSteps/Report/ReportAPMApplicationSummary.cs b/ProcessingSteps/Report/ReportAPMApplicationSummary.cs index 3dee348..a88715e 100644 --- a/ProcessingSteps/Report/ReportAPMApplicationSummary.cs +++ b/ProcessingSteps/Report/ReportAPMApplicationSummary.cs @@ -48,6 +48,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_APM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + return true; } diff --git a/ProcessingSteps/Report/ReportAPMEntities.cs b/ProcessingSteps/Report/ReportAPMEntities.cs index eb6d6ab..57bb779 100644 --- a/ProcessingSteps/Report/ReportAPMEntities.cs +++ b/ProcessingSteps/Report/ReportAPMEntities.cs @@ -137,6 +137,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_APM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + return true; } diff --git a/ProcessingSteps/Report/ReportAPMEntityDashboardScreenshots.cs b/ProcessingSteps/Report/ReportAPMEntityDashboardScreenshots.cs index 0848cee..4527d04 100644 --- a/ProcessingSteps/Report/ReportAPMEntityDashboardScreenshots.cs +++ b/ProcessingSteps/Report/ReportAPMEntityDashboardScreenshots.cs @@ -41,6 +41,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_APM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + return true; } diff --git a/ProcessingSteps/Report/ReportAPMEntityDetails.cs b/ProcessingSteps/Report/ReportAPMEntityDetails.cs index 2d83f6b..c3e6989 100644 --- a/ProcessingSteps/Report/ReportAPMEntityDetails.cs +++ b/ProcessingSteps/Report/ReportAPMEntityDetails.cs @@ -109,6 +109,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_APM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + return true; } @@ -166,7 +169,13 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job #endregion - Parallel.Invoke( + ParallelOptions parallelOptions = new ParallelOptions(); + if (programOptions.ProcessSequentially == true) + { + parallelOptions.MaxDegreeOfParallelism = 1; + } + + Parallel.Invoke(parallelOptions, () => { #region Application @@ -914,35 +923,48 @@ private void fillIndividualEntityMetricReportForEntity( { if (entity.EntityType == APMApplication.ENTITY_TYPE) { - List activityFlowFiltered = FileIOHelper.ReadListFromCSVFile(FilePathMap.ApplicationFlowmapIndexFilePath(jobTarget), new ApplicationActivityFlowReportMap()); - - memoryStreamActivityFlow = FileIOHelper.WriteListToMemoryStream(activityFlowFiltered, new ApplicationActivityFlowReportMap()); + if (File.Exists(FilePathMap.ApplicationFlowmapIndexFilePath(jobTarget)) == true) + { + List activityFlowFiltered = FileIOHelper.ReadListFromCSVFile(FilePathMap.ApplicationFlowmapIndexFilePath(jobTarget), new ApplicationActivityFlowReportMap()); - //EPPlusCSVHelper.ReadCSVFileIntoExcelRange(FilePathMap.ApplicationFlowmapIndexFilePath(jobTarget), 0, sheet, fromRow, 1); + memoryStreamActivityFlow = FileIOHelper.WriteListToMemoryStream(activityFlowFiltered, new ApplicationActivityFlowReportMap()); + } } else if (entity.EntityType == APMTier.ENTITY_TYPE) { - List activityFlowFiltered = FileIOHelper.ReadListFromCSVFile(FilePathMap.TiersFlowmapIndexFilePath(jobTarget), new TierActivityFlowReportMap()).Where(e => e.TierID == entity.EntityID).ToList(); + if (File.Exists(FilePathMap.TiersFlowmapIndexFilePath(jobTarget)) == true) + { + List activityFlowFiltered = FileIOHelper.ReadListFromCSVFile(FilePathMap.TiersFlowmapIndexFilePath(jobTarget), new TierActivityFlowReportMap()).Where(e => e.TierID == entity.EntityID).ToList(); - memoryStreamActivityFlow = FileIOHelper.WriteListToMemoryStream(activityFlowFiltered, new TierActivityFlowReportMap()); + memoryStreamActivityFlow = FileIOHelper.WriteListToMemoryStream(activityFlowFiltered, new TierActivityFlowReportMap()); + } } else if (entity.EntityType == APMNode.ENTITY_TYPE) { - List activityFlowFiltered = FileIOHelper.ReadListFromCSVFile(FilePathMap.NodesFlowmapIndexFilePath(jobTarget), new NodeActivityFlowReportMap()).Where(e => e.NodeID == entity.EntityID).ToList(); + if (File.Exists(FilePathMap.NodesFlowmapIndexFilePath(jobTarget)) == true) + { + List activityFlowFiltered = FileIOHelper.ReadListFromCSVFile(FilePathMap.NodesFlowmapIndexFilePath(jobTarget), new NodeActivityFlowReportMap()).Where(e => e.NodeID == entity.EntityID).ToList(); - memoryStreamActivityFlow = FileIOHelper.WriteListToMemoryStream(activityFlowFiltered, new NodeActivityFlowReportMap()); + memoryStreamActivityFlow = FileIOHelper.WriteListToMemoryStream(activityFlowFiltered, new NodeActivityFlowReportMap()); + } } else if (entity.EntityType == APMBackend.ENTITY_TYPE) { - List activityFlowFiltered = FileIOHelper.ReadListFromCSVFile(FilePathMap.BackendsFlowmapIndexFilePath(jobTarget), new BackendActivityFlowReportMap()).Where(e => e.BackendID == entity.EntityID).ToList(); + if (File.Exists(FilePathMap.BackendsFlowmapIndexFilePath(jobTarget)) == true) + { + List activityFlowFiltered = FileIOHelper.ReadListFromCSVFile(FilePathMap.BackendsFlowmapIndexFilePath(jobTarget), new BackendActivityFlowReportMap()).Where(e => e.BackendID == entity.EntityID).ToList(); - memoryStreamActivityFlow = FileIOHelper.WriteListToMemoryStream(activityFlowFiltered, new BackendActivityFlowReportMap()); + memoryStreamActivityFlow = FileIOHelper.WriteListToMemoryStream(activityFlowFiltered, new BackendActivityFlowReportMap()); + } } else if (entity.EntityType == APMBusinessTransaction.ENTITY_TYPE) { - List activityFlowFiltered = FileIOHelper.ReadListFromCSVFile(FilePathMap.BusinessTransactionsFlowmapIndexFilePath(jobTarget), new BusinessTransactionActivityFlowReportMap()).Where(e => e.BTID == entity.EntityID).ToList(); + if (File.Exists(FilePathMap.BusinessTransactionsFlowmapIndexFilePath(jobTarget)) == true) + { + List activityFlowFiltered = FileIOHelper.ReadListFromCSVFile(FilePathMap.BusinessTransactionsFlowmapIndexFilePath(jobTarget), new BusinessTransactionActivityFlowReportMap()).Where(e => e.BTID == entity.EntityID).ToList(); - memoryStreamActivityFlow = FileIOHelper.WriteListToMemoryStream(activityFlowFiltered, new BusinessTransactionActivityFlowReportMap()); + memoryStreamActivityFlow = FileIOHelper.WriteListToMemoryStream(activityFlowFiltered, new BusinessTransactionActivityFlowReportMap()); + } } } catch (ArgumentNullException ex) diff --git a/ProcessingSteps/Report/ReportAPMFlameGraphs.cs b/ProcessingSteps/Report/ReportAPMFlameGraphs.cs index c6e73e6..eb78dcd 100644 --- a/ProcessingSteps/Report/ReportAPMFlameGraphs.cs +++ b/ProcessingSteps/Report/ReportAPMFlameGraphs.cs @@ -41,6 +41,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_APM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + return true; } @@ -80,7 +83,13 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job #endregion - Parallel.Invoke( + ParallelOptions parallelOptions = new ParallelOptions(); + if (programOptions.ProcessSequentially == true) + { + parallelOptions.MaxDegreeOfParallelism = 1; + } + + Parallel.Invoke(parallelOptions, () => { #region Application diff --git a/ProcessingSteps/Report/ReportAPMFlowmaps.cs b/ProcessingSteps/Report/ReportAPMFlowmaps.cs new file mode 100644 index 0000000..126eade --- /dev/null +++ b/ProcessingSteps/Report/ReportAPMFlowmaps.cs @@ -0,0 +1,411 @@ +using AppDynamics.Dexter.ReportObjectMaps; +using AppDynamics.Dexter.ReportObjects; +using OfficeOpenXml; +using OfficeOpenXml.ConditionalFormatting; +using OfficeOpenXml.Drawing.Chart; +using OfficeOpenXml.Style; +using OfficeOpenXml.Table; +using OfficeOpenXml.Table.PivotTable; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Xml; + +namespace AppDynamics.Dexter.ProcessingSteps +{ + public class ReportAPMFlowmaps : JobStepReportBase + { + #region Constants for report contents + + private const string SHEET_CONTROLLERS = "3.Controllers"; + + private const string SHEET_APPLICATIONS_ACTIVITYFLOW = "4.Applications.Activity Flow"; + private const string SHEET_TIERS_ACTIVITYFLOW = "5.Tiers.Activity Flow"; + private const string SHEET_NODES_ACTIVITYFLOW = "6.Nodes.Activity Flow"; + private const string SHEET_BACKENDS_ACTIVITYFLOW = "7.Backends.Activity Flow"; + private const string SHEET_BUSINESS_TRANSACTIONS_ACTIVITYFLOW = "8.BTs.Activity Flow"; + + private const string TABLE_CONTROLLERS = "t_Controllers"; + + private const string TABLE_APPLICATIONS_ACTIVITYFLOW = "t_Applications_ActivityFlow"; + private const string TABLE_TIERS_ACTIVITYFLOW = "t_Tiers_ActivityFlow"; + private const string TABLE_NODES_ACTIVITYFLOW = "t_Nodes_ActivityFlow"; + private const string TABLE_BACKENDS_ACTIVITYFLOW = "t_Backends_ActivityFlow"; + private const string TABLE_BUSINESS_TRANSACTIONS_ACTIVITYFLOW = "t_BusinessTransactions_ActivityFlow"; + + private const int LIST_SHEET_START_TABLE_AT = 17; + private const int PIVOT_SHEET_START_PIVOT_AT = 7; + private const int PIVOT_SHEET_CHART_HEIGHT = 14; + + #endregion + + public override bool Execute(ProgramOptions programOptions, JobConfiguration jobConfiguration) + { + Stopwatch stopWatch = new Stopwatch(); + stopWatch.Start(); + + StepTiming stepTimingFunction = new StepTiming(); + stepTimingFunction.JobFileName = programOptions.OutputJobFilePath; + stepTimingFunction.StepName = jobConfiguration.Status.ToString(); + stepTimingFunction.StepID = (int)jobConfiguration.Status; + stepTimingFunction.StartTime = DateTime.Now; + stepTimingFunction.NumEntities = jobConfiguration.Target.Count; + + this.DisplayJobStepStartingStatus(jobConfiguration); + + FilePathMap = new FilePathMap(programOptions, jobConfiguration); + + if (this.ShouldExecute(programOptions, jobConfiguration) == false) + { + return true; + } + + if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_APM) == 0) + { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + + return true; + } + + try + { + loggerConsole.Info("Prepare Entity Flowmaps Report File"); + + #region Prepare the report package + + // Prepare package + ExcelPackage excelReport = new ExcelPackage(); + excelReport.Workbook.Properties.Author = String.Format("AppDynamics DEXTER {0}", Assembly.GetEntryAssembly().GetName().Version); + excelReport.Workbook.Properties.Title = "AppDynamics DEXTER Entity Flowmaps Report"; + excelReport.Workbook.Properties.Subject = programOptions.JobName; + + excelReport.Workbook.Properties.Comments = String.Format("Targets={0}\nFrom={1:o}\nTo={2:o}", jobConfiguration.Target.Count, jobConfiguration.Input.TimeRange.From, jobConfiguration.Input.TimeRange.To); + + #endregion + + #region Parameters sheet + + // Parameters sheet + ExcelWorksheet sheet = excelReport.Workbook.Worksheets.Add(SHEET_PARAMETERS); + + var hyperLinkStyle = sheet.Workbook.Styles.CreateNamedStyle("HyperLinkStyle"); + hyperLinkStyle.Style.Font.UnderLineType = ExcelUnderLineType.Single; + hyperLinkStyle.Style.Font.Color.SetColor(colorBlueForHyperlinks); + + fillReportParametersSheet(sheet, jobConfiguration, "AppDynamics DEXTER Entity Flowmaps Report"); + + #endregion + + #region TOC sheet + + // Navigation sheet with link to other sheets + sheet = excelReport.Workbook.Worksheets.Add(SHEET_TOC); + + #endregion + + #region Entity sheets and their associated pivots + + // Entity sheets + sheet = excelReport.Workbook.Worksheets.Add(SHEET_CONTROLLERS); + sheet.Cells[1, 1].Value = "Table of Contents"; + sheet.Cells[1, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TOC); + sheet.Cells[1, 2].StyleName = "HyperLinkStyle"; + sheet.View.FreezePanes(LIST_SHEET_START_TABLE_AT - 13 + 1, 1); + + sheet = excelReport.Workbook.Worksheets.Add(SHEET_APPLICATIONS_ACTIVITYFLOW); + sheet.Cells[1, 1].Value = "Table of Contents"; + sheet.Cells[1, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TOC); + sheet.Cells[1, 2].StyleName = "HyperLinkStyle"; + sheet.View.FreezePanes(LIST_SHEET_START_TABLE_AT - 13 + 1, 1); + + sheet = excelReport.Workbook.Worksheets.Add(SHEET_TIERS_ACTIVITYFLOW); + sheet.Cells[1, 1].Value = "Table of Contents"; + sheet.Cells[1, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TOC); + sheet.Cells[1, 2].StyleName = "HyperLinkStyle"; + sheet.View.FreezePanes(LIST_SHEET_START_TABLE_AT - 13 + 1, 1); + + sheet = excelReport.Workbook.Worksheets.Add(SHEET_NODES_ACTIVITYFLOW); + sheet.Cells[1, 1].Value = "Table of Contents"; + sheet.Cells[1, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TOC); + sheet.Cells[1, 2].StyleName = "HyperLinkStyle"; + sheet.View.FreezePanes(LIST_SHEET_START_TABLE_AT - 13 + 1, 1); + + sheet = excelReport.Workbook.Worksheets.Add(SHEET_BACKENDS_ACTIVITYFLOW); + sheet.Cells[1, 1].Value = "Table of Contents"; + sheet.Cells[1, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TOC); + sheet.Cells[1, 2].StyleName = "HyperLinkStyle"; + sheet.View.FreezePanes(LIST_SHEET_START_TABLE_AT - 13 + 1, 1); + + sheet = excelReport.Workbook.Worksheets.Add(SHEET_BUSINESS_TRANSACTIONS_ACTIVITYFLOW); + sheet.Cells[1, 1].Value = "Table of Contents"; + sheet.Cells[1, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TOC); + sheet.Cells[1, 2].StyleName = "HyperLinkStyle"; + sheet.View.FreezePanes(LIST_SHEET_START_TABLE_AT - 13 + 1, 1); + + #endregion + + loggerConsole.Info("Fill Entity Flowmaps Report File"); + + #region Report file variables + + ExcelRangeBase range = null; + ExcelTable table = null; + + #endregion + + #region Controllers + + loggerConsole.Info("List of Controllers"); + + sheet = excelReport.Workbook.Worksheets[SHEET_CONTROLLERS]; + EPPlusCSVHelper.ReadCSVFileIntoExcelRange(FilePathMap.ControllerSummaryReportFilePath(), 0, sheet, LIST_SHEET_START_TABLE_AT - 13, 1); + + #endregion + + #region Applications + + loggerConsole.Info("Applications Flowmap"); + + sheet = excelReport.Workbook.Worksheets[SHEET_APPLICATIONS_ACTIVITYFLOW]; + EPPlusCSVHelper.ReadCSVFileIntoExcelRange(FilePathMap.ApplicationsFlowmapReportFilePath(), 0, sheet, LIST_SHEET_START_TABLE_AT - 13, 1); + + #endregion + + #region Tiers + + loggerConsole.Info("Tiers Flowmap"); + + sheet = excelReport.Workbook.Worksheets[SHEET_TIERS_ACTIVITYFLOW]; + EPPlusCSVHelper.ReadCSVFileIntoExcelRange(FilePathMap.TiersFlowmapReportFilePath(), 0, sheet, LIST_SHEET_START_TABLE_AT - 13, 1); + + #endregion + + #region Nodes + + loggerConsole.Info("Nodes Flowmap"); + + sheet = excelReport.Workbook.Worksheets[SHEET_NODES_ACTIVITYFLOW]; + EPPlusCSVHelper.ReadCSVFileIntoExcelRange(FilePathMap.NodesFlowmapReportFilePath(), 0, sheet, LIST_SHEET_START_TABLE_AT - 13, 1); + + #endregion + + #region Backends + + loggerConsole.Info("Backends Flowmap"); + + sheet = excelReport.Workbook.Worksheets[SHEET_BACKENDS_ACTIVITYFLOW]; + EPPlusCSVHelper.ReadCSVFileIntoExcelRange(FilePathMap.BackendsFlowmapReportFilePath(), 0, sheet, LIST_SHEET_START_TABLE_AT - 13, 1); + + #endregion + + #region Business Transactions + + loggerConsole.Info("Business Transactions Flowmap"); + + sheet = excelReport.Workbook.Worksheets[SHEET_BUSINESS_TRANSACTIONS_ACTIVITYFLOW]; + EPPlusCSVHelper.ReadCSVFileIntoExcelRange(FilePathMap.BusinessTransactionsFlowmapReportFilePath(), 0, sheet, LIST_SHEET_START_TABLE_AT - 13, 1); + + #endregion + + loggerConsole.Info("Finalize Entity Flowmaps Report File"); + + #region Controllers sheet + + // Make table + sheet = excelReport.Workbook.Worksheets[SHEET_CONTROLLERS]; + logger.Info("{0} Sheet ({1} rows)", sheet.Name, sheet.Dimension.Rows); + loggerConsole.Info("{0} Sheet ({1} rows)", sheet.Name, sheet.Dimension.Rows); + if (sheet.Dimension.Rows > LIST_SHEET_START_TABLE_AT - 13) + { + range = sheet.Cells[LIST_SHEET_START_TABLE_AT - 13, 1, sheet.Dimension.Rows, sheet.Dimension.Columns]; + table = sheet.Tables.Add(range, TABLE_CONTROLLERS); + table.ShowHeader = true; + table.TableStyle = TableStyles.Medium2; + table.ShowFilter = true; + table.ShowTotal = false; + + sheet.Column(table.Columns["Controller"].Position + 1).Width = 25; + sheet.Column(table.Columns["Version"].Position + 1).Width = 15; + } + + #endregion + + #region Applications + + // Make table + sheet = excelReport.Workbook.Worksheets[SHEET_APPLICATIONS_ACTIVITYFLOW]; + logger.Info("{0} Sheet ({1} rows)", sheet.Name, sheet.Dimension.Rows); + loggerConsole.Info("{0} Sheet ({1} rows)", sheet.Name, sheet.Dimension.Rows); + if (sheet.Dimension.Rows > LIST_SHEET_START_TABLE_AT - 13) + { + range = sheet.Cells[LIST_SHEET_START_TABLE_AT - 13, 1, sheet.Dimension.Rows, sheet.Dimension.Columns]; + table = sheet.Tables.Add(range, TABLE_APPLICATIONS_ACTIVITYFLOW); + table.ShowHeader = true; + table.TableStyle = TableStyles.Medium2; + table.ShowFilter = true; + table.ShowTotal = false; + + adjustColumnsOfActivityFlowRowTableInMetricReport(APMApplication.ENTITY_TYPE, sheet, table); + } + + #endregion + + #region Tiers + + // Make table + sheet = excelReport.Workbook.Worksheets[SHEET_TIERS_ACTIVITYFLOW]; + logger.Info("{0} Sheet ({1} rows)", sheet.Name, sheet.Dimension.Rows); + loggerConsole.Info("{0} Sheet ({1} rows)", sheet.Name, sheet.Dimension.Rows); + if (sheet.Dimension.Rows > LIST_SHEET_START_TABLE_AT - 13) + { + range = sheet.Cells[LIST_SHEET_START_TABLE_AT - 13, 1, sheet.Dimension.Rows, sheet.Dimension.Columns]; + table = sheet.Tables.Add(range, TABLE_TIERS_ACTIVITYFLOW); + table.ShowHeader = true; + table.TableStyle = TableStyles.Medium2; + table.ShowFilter = true; + table.ShowTotal = false; + + adjustColumnsOfActivityFlowRowTableInMetricReport(APMTier.ENTITY_TYPE, sheet, table); + } + + #endregion + + #region Nodes + + // Make table + sheet = excelReport.Workbook.Worksheets[SHEET_NODES_ACTIVITYFLOW]; + logger.Info("{0} Sheet ({1} rows)", sheet.Name, sheet.Dimension.Rows); + loggerConsole.Info("{0} Sheet ({1} rows)", sheet.Name, sheet.Dimension.Rows); + if (sheet.Dimension.Rows > LIST_SHEET_START_TABLE_AT - 13) + { + range = sheet.Cells[LIST_SHEET_START_TABLE_AT - 13, 1, sheet.Dimension.Rows, sheet.Dimension.Columns]; + table = sheet.Tables.Add(range, TABLE_NODES_ACTIVITYFLOW); + table.ShowHeader = true; + table.TableStyle = TableStyles.Medium2; + table.ShowFilter = true; + table.ShowTotal = false; + + adjustColumnsOfActivityFlowRowTableInMetricReport(APMNode.ENTITY_TYPE, sheet, table); + } + + #endregion + + #region Backends + + // Make table + sheet = excelReport.Workbook.Worksheets[SHEET_BACKENDS_ACTIVITYFLOW]; + logger.Info("{0} Sheet ({1} rows)", sheet.Name, sheet.Dimension.Rows); + loggerConsole.Info("{0} Sheet ({1} rows)", sheet.Name, sheet.Dimension.Rows); + if (sheet.Dimension.Rows > LIST_SHEET_START_TABLE_AT - 13) + { + range = sheet.Cells[LIST_SHEET_START_TABLE_AT - 13, 1, sheet.Dimension.Rows, sheet.Dimension.Columns]; + table = sheet.Tables.Add(range, TABLE_BACKENDS_ACTIVITYFLOW); + table.ShowHeader = true; + table.TableStyle = TableStyles.Medium2; + table.ShowFilter = true; + table.ShowTotal = false; + + adjustColumnsOfActivityFlowRowTableInMetricReport(APMBackend.ENTITY_TYPE, sheet, table); + } + + #endregion + + #region Business Transactions + + // Make table + sheet = excelReport.Workbook.Worksheets[SHEET_BUSINESS_TRANSACTIONS_ACTIVITYFLOW]; + logger.Info("{0} Sheet ({1} rows)", sheet.Name, sheet.Dimension.Rows); + loggerConsole.Info("{0} Sheet ({1} rows)", sheet.Name, sheet.Dimension.Rows); + if (sheet.Dimension.Rows > LIST_SHEET_START_TABLE_AT - 13) + { + range = sheet.Cells[LIST_SHEET_START_TABLE_AT - 13, 1, sheet.Dimension.Rows, sheet.Dimension.Columns]; + table = sheet.Tables.Add(range, TABLE_BUSINESS_TRANSACTIONS_ACTIVITYFLOW); + table.ShowHeader = true; + table.TableStyle = TableStyles.Medium2; + table.ShowFilter = true; + table.ShowTotal = false; + + adjustColumnsOfActivityFlowRowTableInMetricReport(APMBusinessTransaction.ENTITY_TYPE, sheet, table); + } + + #endregion + + #region TOC sheet + + // TOC sheet again + sheet = excelReport.Workbook.Worksheets[SHEET_TOC]; + fillTableOfContentsSheet(sheet, excelReport); + + #endregion + + #region Save file + + FileIOHelper.CreateFolder(FilePathMap.ReportFolderPath()); + + string reportFilePath = FilePathMap.FlowmapsExcelReportFilePath(jobConfiguration.Input.TimeRange); + logger.Info("Saving Excel report {0}", reportFilePath); + loggerConsole.Info("Saving Excel report {0}", reportFilePath); + + try + { + // Save full report Excel files + excelReport.SaveAs(new FileInfo(reportFilePath)); + } + catch (InvalidOperationException ex) + { + logger.Warn("Unable to save Excel file {0}", reportFilePath); + logger.Warn(ex); + loggerConsole.Warn("Unable to save Excel file {0}", reportFilePath); + } + + #endregion + + return true; + } + catch (Exception ex) + { + logger.Error(ex); + loggerConsole.Error(ex); + + return false; + } + finally + { + stopWatch.Stop(); + + this.DisplayJobStepEndedStatus(jobConfiguration, stopWatch); + + stepTimingFunction.EndTime = DateTime.Now; + stepTimingFunction.Duration = stopWatch.Elapsed; + stepTimingFunction.DurationMS = stopWatch.ElapsedMilliseconds; + + List stepTimings = new List(1); + stepTimings.Add(stepTimingFunction); + FileIOHelper.WriteListToCSVFile(stepTimings, new StepTimingReportMap(), FilePathMap.StepTimingReportFilePath(), true); + } + } + + public override bool ShouldExecute(ProgramOptions programOptions, JobConfiguration jobConfiguration) + { + logger.Trace("LicensedReports.Flowmaps={0}", programOptions.LicensedReports.Flowmaps); + loggerConsole.Trace("LicensedReports.Flowmaps={0}", programOptions.LicensedReports.Flowmaps); + if (programOptions.LicensedReports.EntityMetrics == false) + { + loggerConsole.Warn("Not licensed for entity flowmaps"); + return false; + } + + logger.Trace("Input.Flowmaps={0}", jobConfiguration.Input.Flowmaps); + loggerConsole.Trace("Input.Flowmaps={0}", jobConfiguration.Input.Flowmaps); + if (jobConfiguration.Input.Flowmaps == false) + { + loggerConsole.Trace("Skipping report of entity flowmaps"); + } + return (jobConfiguration.Input.Flowmaps == true); + } + } +} diff --git a/ProcessingSteps/Report/ReportAPMMetricGraphs.cs b/ProcessingSteps/Report/ReportAPMMetricGraphs.cs index e4c84a3..8f57c5a 100644 --- a/ProcessingSteps/Report/ReportAPMMetricGraphs.cs +++ b/ProcessingSteps/Report/ReportAPMMetricGraphs.cs @@ -4,6 +4,7 @@ using OfficeOpenXml.Drawing.Chart; using OfficeOpenXml.Style; using OfficeOpenXml.Table; +using OfficeOpenXml.Table.PivotTable; using System; using System.Collections.Generic; using System.Diagnostics; @@ -38,6 +39,8 @@ public class ReportAPMMetricGraphs : JobStepReportBase private const string SHEET_ENTITIES_METRICS = "Entity.Metrics"; + private const string SHEET_PIVOT_GRAPH_METRICS_ALL_ENTITIES = "12.Graph.{0}"; + private const string TABLE_CONTROLLERS = "t_Controllers"; // Metric data tables from metric.values.csv @@ -59,7 +62,13 @@ public class ReportAPMMetricGraphs : JobStepReportBase private const string TABLE_INFORMATION_POINTS = "t_InformationPoints_{0}"; private const string TABLE_INFORMATION_POINTS_SCATTER = "t_InformationPointsScatter"; + private const string PIVOT_GRAPH_METRICS_ALL_ENTITIES = "p_All_{0}"; + + private const string GRAPH_METRICS_ALL_ENTITIES = "g_All_{0}"; + private const int LIST_SHEET_START_TABLE_AT = 4; + private const int PIVOT_SHEET_START_PIVOT_AT = 7; + private const int PIVOT_SHEET_CHART_HEIGHT = 14; private const int GRAPH_SHEET_START_TABLE_AT = 6; // Hourly graph data @@ -111,6 +120,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_APM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + return true; } @@ -147,7 +159,13 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job loggerConsole.Info("Prepare Entity Metrics Graphs Report with {0} metrics", entityMetricExtractMappingList.Count); - Parallel.Invoke( + ParallelOptions parallelOptions = new ParallelOptions(); + if (programOptions.ProcessSequentially == true) + { + parallelOptions.MaxDegreeOfParallelism = 1; + } + + Parallel.Invoke(parallelOptions, () => { #region Application @@ -163,6 +181,7 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job fillMetricGraphsForEntityType(excelReport, entityMetricExtractMappingListFiltered, applicationsList.OfType().ToList(), jobConfiguration, jobTarget, APMApplication.ENTITY_FOLDER, APMApplication.ENTITY_TYPE); fillTransactionalScatterPlotsForEntityType(excelReport, entityMetricExtractMappingListFiltered, applicationsList.OfType().ToList(), jobConfiguration, jobTarget, APMApplication.ENTITY_FOLDER, APMApplication.ENTITY_TYPE); + fillPivotGraphsForEntityType(excelReport, entityMetricExtractMappingListFiltered, applicationsList.OfType().ToList(), jobConfiguration, jobTarget, APMApplication.ENTITY_FOLDER, APMApplication.ENTITY_TYPE); finalizeAndSaveIndividualEntityMetricReport(excelReport, FilePathMap.EntityTypeMetricGraphsExcelReportFilePath(applicationsList[0], jobTarget, jobConfiguration.Input.TimeRange, true)); @@ -186,6 +205,7 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job fillMetricGraphsForEntityType(excelReport, entityMetricExtractMappingListFiltered, tiersList.OfType().ToList(), jobConfiguration, jobTarget, APMTier.ENTITY_FOLDER, APMTier.ENTITY_TYPE); fillTransactionalScatterPlotsForEntityType(excelReport, entityMetricExtractMappingListFiltered, tiersList.OfType().ToList(), jobConfiguration, jobTarget, APMTier.ENTITY_FOLDER, APMTier.ENTITY_TYPE); + fillPivotGraphsForEntityType(excelReport, entityMetricExtractMappingListFiltered, tiersList.OfType().ToList(), jobConfiguration, jobTarget, APMTier.ENTITY_FOLDER, APMTier.ENTITY_TYPE); finalizeAndSaveIndividualEntityMetricReport(excelReport, FilePathMap.EntityTypeMetricGraphsExcelReportFilePath(tiersList[0], jobTarget, jobConfiguration.Input.TimeRange, true)); @@ -210,6 +230,7 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job fillMetricGraphsForEntityType(excelReport, entityMetricExtractMappingListFiltered, nodesList.OfType().ToList(), jobConfiguration, jobTarget, APMNode.ENTITY_FOLDER, APMNode.ENTITY_TYPE); fillTransactionalScatterPlotsForEntityType(excelReport, entityMetricExtractMappingListFiltered, nodesList.OfType().ToList(), jobConfiguration, jobTarget, APMNode.ENTITY_FOLDER, APMNode.ENTITY_TYPE); + fillPivotGraphsForEntityType(excelReport, entityMetricExtractMappingListFiltered, nodesList.OfType().ToList(), jobConfiguration, jobTarget, APMNode.ENTITY_FOLDER, APMNode.ENTITY_TYPE); finalizeAndSaveIndividualEntityMetricReport(excelReport, FilePathMap.EntityTypeMetricGraphsExcelReportFilePath(nodesList[0], jobTarget, jobConfiguration.Input.TimeRange, true)); @@ -233,6 +254,7 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job fillMetricGraphsForEntityType(excelReport, entityMetricExtractMappingListFiltered, backendsList.OfType().ToList(), jobConfiguration, jobTarget, APMBackend.ENTITY_FOLDER, APMBackend.ENTITY_TYPE); fillTransactionalScatterPlotsForEntityType(excelReport, entityMetricExtractMappingListFiltered, backendsList.OfType().ToList(), jobConfiguration, jobTarget, APMBackend.ENTITY_FOLDER, APMBackend.ENTITY_TYPE); + fillPivotGraphsForEntityType(excelReport, entityMetricExtractMappingListFiltered, backendsList.OfType().ToList(), jobConfiguration, jobTarget, APMBackend.ENTITY_FOLDER, APMBackend.ENTITY_TYPE); finalizeAndSaveIndividualEntityMetricReport(excelReport, FilePathMap.EntityTypeMetricGraphsExcelReportFilePath(backendsList[0], jobTarget, jobConfiguration.Input.TimeRange, true)); @@ -256,6 +278,7 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job fillMetricGraphsForEntityType(excelReport, entityMetricExtractMappingListFiltered, businessTransactionsList.OfType().ToList(), jobConfiguration, jobTarget, APMBusinessTransaction.ENTITY_FOLDER, APMBusinessTransaction.ENTITY_TYPE); fillTransactionalScatterPlotsForEntityType(excelReport, entityMetricExtractMappingListFiltered, businessTransactionsList.OfType().ToList(), jobConfiguration, jobTarget, APMBusinessTransaction.ENTITY_FOLDER, APMBusinessTransaction.ENTITY_TYPE); + fillPivotGraphsForEntityType(excelReport, entityMetricExtractMappingListFiltered, businessTransactionsList.OfType().ToList(), jobConfiguration, jobTarget, APMBusinessTransaction.ENTITY_FOLDER, APMBusinessTransaction.ENTITY_TYPE); finalizeAndSaveIndividualEntityMetricReport(excelReport, FilePathMap.EntityTypeMetricGraphsExcelReportFilePath(businessTransactionsList[0], jobTarget, jobConfiguration.Input.TimeRange, true)); @@ -279,6 +302,7 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job fillMetricGraphsForEntityType(excelReport, entityMetricExtractMappingListFiltered, serviceEndpointsList.OfType().ToList(), jobConfiguration, jobTarget, APMServiceEndpoint.ENTITY_FOLDER, APMServiceEndpoint.ENTITY_TYPE); fillTransactionalScatterPlotsForEntityType(excelReport, entityMetricExtractMappingListFiltered, serviceEndpointsList.OfType().ToList(), jobConfiguration, jobTarget, APMServiceEndpoint.ENTITY_FOLDER, APMServiceEndpoint.ENTITY_TYPE); + fillPivotGraphsForEntityType(excelReport, entityMetricExtractMappingListFiltered, serviceEndpointsList.OfType().ToList(), jobConfiguration, jobTarget, APMServiceEndpoint.ENTITY_FOLDER, APMServiceEndpoint.ENTITY_TYPE); finalizeAndSaveIndividualEntityMetricReport(excelReport, FilePathMap.EntityTypeMetricGraphsExcelReportFilePath(serviceEndpointsList[0], jobTarget, jobConfiguration.Input.TimeRange, true)); @@ -301,6 +325,7 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job fillMetricValueTablesForEntityType(excelReport, SHEET_ENTITIES_METRICS, entityMetricExtractMappingListFiltered, jobTarget, APMError.ENTITY_FOLDER); fillMetricGraphsForEntityType(excelReport, entityMetricExtractMappingListFiltered, errorsList.OfType().ToList(), jobConfiguration, jobTarget, APMError.ENTITY_FOLDER, APMError.ENTITY_TYPE); + fillPivotGraphsForEntityType(excelReport, entityMetricExtractMappingListFiltered, errorsList.OfType().ToList(), jobConfiguration, jobTarget, APMError.ENTITY_FOLDER, APMError.ENTITY_TYPE); finalizeAndSaveIndividualEntityMetricReport(excelReport, FilePathMap.EntityTypeMetricGraphsExcelReportFilePath(errorsList[0], jobTarget, jobConfiguration.Input.TimeRange, true)); @@ -324,6 +349,7 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job fillMetricGraphsForEntityType(excelReport, entityMetricExtractMappingListFiltered, informationPointsList.OfType().ToList(), jobConfiguration, jobTarget, APMInformationPoint.ENTITY_FOLDER, APMInformationPoint.ENTITY_TYPE); fillTransactionalScatterPlotsForEntityType(excelReport, entityMetricExtractMappingListFiltered, informationPointsList.OfType().ToList(), jobConfiguration, jobTarget, APMInformationPoint.ENTITY_FOLDER, APMInformationPoint.ENTITY_TYPE); + fillPivotGraphsForEntityType(excelReport, entityMetricExtractMappingListFiltered, informationPointsList.OfType().ToList(), jobConfiguration, jobTarget, APMInformationPoint.ENTITY_FOLDER, APMInformationPoint.ENTITY_TYPE); finalizeAndSaveIndividualEntityMetricReport(excelReport, FilePathMap.EntityTypeMetricGraphsExcelReportFilePath(informationPointsList[0], jobTarget, jobConfiguration.Input.TimeRange, true)); @@ -677,11 +703,17 @@ private void fillMetricGraphsForEntityType( #region Add outlines and time range labels for each of the hourly ranges - for (int i = 0; i < jobConfiguration.Input.HourlyTimeRanges.Count; i++) + int indexOfTimeRangeToStartWith = 0; + if (jobConfiguration.Input.HourlyTimeRanges.Count > 8) + { + indexOfTimeRangeToStartWith = jobConfiguration.Input.HourlyTimeRanges.Count - 8; + } + + for (int indexOfTimeRange = indexOfTimeRangeToStartWith; indexOfTimeRange < jobConfiguration.Input.HourlyTimeRanges.Count; indexOfTimeRange++) { - JobTimeRange jobTimeRange = jobConfiguration.Input.HourlyTimeRanges[i]; + JobTimeRange jobTimeRange = jobConfiguration.Input.HourlyTimeRanges[indexOfTimeRange]; - int columnIndexTimeRangeStart = columnsBeforeFirstHourRange + 1 + i * columnsBetweenHourRanges + i * numCellsPerHourRange; + int columnIndexTimeRangeStart = columnsBeforeFirstHourRange + 1 + (indexOfTimeRange - indexOfTimeRangeToStartWith) * (columnsBetweenHourRanges + numCellsPerHourRange); int columnIndexTimeRangeEnd = columnIndexTimeRangeStart + numCellsPerHourRange; for (int columnIndex = columnIndexTimeRangeStart; columnIndex < columnIndexTimeRangeEnd; columnIndex++) { @@ -762,7 +794,7 @@ private void fillMetricGraphsForEntityType( bool entityHasActivity = false; // Output graphs for every hour range one at a time - for (int indexOfTimeRange = 0; indexOfTimeRange < jobConfiguration.Input.HourlyTimeRanges.Count; indexOfTimeRange++) + for (int indexOfTimeRange = indexOfTimeRangeToStartWith; indexOfTimeRange < jobConfiguration.Input.HourlyTimeRanges.Count; indexOfTimeRange++) { JobTimeRange jobTimeRange = jobConfiguration.Input.HourlyTimeRanges[indexOfTimeRange]; @@ -779,7 +811,7 @@ private void fillMetricGraphsForEntityType( // Output headers for each of the metrics // Calculate where the index of the column value (ART, Total etc) are going to be output // Sum of (Offset of tables + offset of the time range + number of columns between + the number of the metric - int columnIndexOfCurrentRangeBegin = columnsBeforeFirstHourRange + 1 + indexOfTimeRange * columnsBetweenHourRanges + indexOfTimeRange * numCellsPerHourRange; + int columnIndexOfCurrentRangeBegin = columnsBeforeFirstHourRange + 1 + (indexOfTimeRange - indexOfTimeRangeToStartWith) * (columnsBetweenHourRanges + numCellsPerHourRange); int columnIndexOfValueOfCurrentMetric = columnIndexOfCurrentRangeBegin + indexOfMetricMapping * 2; sheetGraphs.Cells[entityTableHeaderRow - 1, columnIndexOfValueOfCurrentMetric].Value = metricExtractMapping.MetricName; sheetGraphs.Cells[entityTableHeaderRow - 1, columnIndexOfValueOfCurrentMetric].StyleName = "MetricNameStyle"; @@ -1129,11 +1161,17 @@ private void fillTransactionalScatterPlotsForEntityType( #region Add outlines and time range labels for each of the hourly ranges - for (int i = 0; i < jobConfiguration.Input.HourlyTimeRanges.Count; i++) + int indexOfTimeRangeToStartWith = 0; + if (jobConfiguration.Input.HourlyTimeRanges.Count > 8) { - JobTimeRange jobTimeRange = jobConfiguration.Input.HourlyTimeRanges[i]; + indexOfTimeRangeToStartWith = jobConfiguration.Input.HourlyTimeRanges.Count - 8; + } + + for (int indexOfTimeRange = indexOfTimeRangeToStartWith; indexOfTimeRange < jobConfiguration.Input.HourlyTimeRanges.Count; indexOfTimeRange++) + { + JobTimeRange jobTimeRange = jobConfiguration.Input.HourlyTimeRanges[indexOfTimeRange]; - int columnIndexTimeRangeStart = columnsBeforeFirstHourRange + 1 + i * columnsBetweenHourRanges + i * numCellsPerHourRange; + int columnIndexTimeRangeStart = columnsBeforeFirstHourRange + 1 + (indexOfTimeRange - indexOfTimeRangeToStartWith) * (columnsBetweenHourRanges + numCellsPerHourRange); int columnIndexTimeRangeEnd = columnIndexTimeRangeStart + numCellsPerHourRange; for (int columnIndex = columnIndexTimeRangeStart; columnIndex < columnIndexTimeRangeEnd; columnIndex++) { @@ -1216,13 +1254,13 @@ private void fillTransactionalScatterPlotsForEntityType( bool entityHasActivity = false; // Output graphs for every hour range one at a time - for (int indexOfTimeRange = 0; indexOfTimeRange < jobConfiguration.Input.HourlyTimeRanges.Count; indexOfTimeRange++) + for (int indexOfTimeRange = indexOfTimeRangeToStartWith; indexOfTimeRange < jobConfiguration.Input.HourlyTimeRanges.Count; indexOfTimeRange++) { JobTimeRange jobTimeRange = jobConfiguration.Input.HourlyTimeRanges[indexOfTimeRange]; #region Headers and legend for each range - int columnIndexOfCurrentRangeBegin = columnsBeforeFirstHourRange + 1 + indexOfTimeRange * columnsBetweenHourRanges + indexOfTimeRange * numCellsPerHourRange; + int columnIndexOfCurrentRangeBegin = columnsBeforeFirstHourRange + 1 + (indexOfTimeRange - indexOfTimeRangeToStartWith) * (columnsBetweenHourRanges + numCellsPerHourRange); #endregion @@ -1437,6 +1475,58 @@ private void fillTransactionalScatterPlotsForEntityType( } } + private void fillPivotGraphsForEntityType( + ExcelPackage excelReportMetrics, + List entityMetricExtractMappingList, + List entityList, + JobConfiguration jobConfiguration, + JobTarget jobTarget, + string entityFolderName, + string entityType) + { + ExcelWorksheet sheetMetrics = excelReportMetrics.Workbook.Worksheets[SHEET_ENTITIES_METRICS]; + + // Load each of the metrics in the mapping table that apply to this entity type + foreach (MetricExtractMapping metricExtractMapping in entityMetricExtractMappingList) + { + string worksheetName = getExcelTableOrSheetSafeString(String.Format(SHEET_PIVOT_GRAPH_METRICS_ALL_ENTITIES, metricExtractMapping.FolderName)); + ExcelWorksheet sheetGraphs = excelReportMetrics.Workbook.Worksheets.Add(worksheetName); + sheetGraphs.Cells[1, 1].Value = "Table of Contents"; + sheetGraphs.Cells[1, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TOC); + sheetGraphs.Cells[1, 2].StyleName = "HyperLinkStyle"; + sheetGraphs.Cells[2, 1].Value = "See Data"; + sheetGraphs.Cells[2, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_ENTITIES_METRICS); + sheetGraphs.Cells[2, 2].StyleName = "HyperLinkStyle"; + sheetGraphs.View.FreezePanes(PIVOT_SHEET_START_PIVOT_AT + PIVOT_SHEET_CHART_HEIGHT + 3, 1); + excelReportMetrics.Workbook.Worksheets.MoveBefore(worksheetName, SHEET_ENTITIES_METRICS); + + sheetGraphs.Cells[1, 3].Value = metricExtractMapping.MetricName; + sheetGraphs.Cells[2, 3].Value = metricExtractMapping.MetricPath; + + ExcelTable tableMetrics = sheetMetrics.Tables[String.Format(TABLE_METRIC_VALUES, entityFolderName, metricExtractMapping.FolderName)]; + if (tableMetrics != null) + { + ExcelRangeBase rangeTableMetrics = (ExcelRangeBase)tableMetrics.Address; + ExcelPivotTable pivot= sheetGraphs.PivotTables.Add(sheetGraphs.Cells[PIVOT_SHEET_START_PIVOT_AT + PIVOT_SHEET_CHART_HEIGHT + 1, 1], rangeTableMetrics, String.Format(PIVOT_GRAPH_METRICS_ALL_ENTITIES, metricExtractMapping.FolderName)); + setDefaultPivotTableSettings(pivot); + ExcelPivotTableField fieldR = pivot.RowFields.Add(pivot.Fields["EventTimeStamp"]); + fieldR.AddDateGrouping(eDateGroupBy.Days | eDateGroupBy.Hours | eDateGroupBy.Minutes); + fieldR.Compact = false; + fieldR.Outline = false; + addColumnFieldToPivot(pivot, "EntityName", eSortType.Ascending); + addDataFieldToPivot(pivot, "Value", DataFieldFunctions.Average); + + ExcelChart chart = sheetGraphs.Drawings.AddChart(String.Format(GRAPH_METRICS_ALL_ENTITIES, metricExtractMapping.FolderName), eChartType.Line, pivot); + chart.SetPosition(2, 0, 0, 0); + chart.SetSize(1200, 350); + + sheetGraphs.Column(1).Width = 20; + sheetGraphs.Column(2).Width = 20; + sheetGraphs.Column(3).Width = 20; + } + } + } + private Dictionary> indexMetricValueLocationsForFasterAccess( JobConfiguration jobConfiguration, JobTarget jobTarget, diff --git a/ProcessingSteps/Report/ReportAPMMetrics.cs b/ProcessingSteps/Report/ReportAPMMetrics.cs index 30d260e..88a10be 100644 --- a/ProcessingSteps/Report/ReportAPMMetrics.cs +++ b/ProcessingSteps/Report/ReportAPMMetrics.cs @@ -48,12 +48,6 @@ public class ReportAPMMetrics : JobStepReportBase private const string SHEET_INFORMATION_POINTS_HOURLY = "11.Information Points.Hourly"; private const string SHEET_INFORMATION_POINTS_PERF_PIVOT = "11.Information Points.Perf"; - private const string SHEET_APPLICATIONS_ACTIVITYFLOW = "4.Applications.Activity Flow"; - private const string SHEET_TIERS_ACTIVITYFLOW = "5.Tiers.Activity Flow"; - private const string SHEET_NODES_ACTIVITYFLOW = "6.Nodes.Activity Flow"; - private const string SHEET_BACKENDS_ACTIVITYFLOW = "7.Backends.Activity Flow"; - private const string SHEET_BUSINESS_TRANSACTIONS_ACTIVITYFLOW = "8.BTs.Activity Flow"; - private const string TABLE_CONTROLLERS = "t_Controllers"; private const string TABLE_APPLICATIONS_FULL = "t_Applications_Full"; private const string TABLE_APPLICATIONS_HOURLY = "t_Applications_Hourly"; @@ -72,12 +66,6 @@ public class ReportAPMMetrics : JobStepReportBase private const string TABLE_INFORMATION_POINTS_FULL = "t_InformationPoints_Full"; private const string TABLE_INFORMATION_POINTS_HOURLY = "t_InformationPoints_Hourly"; - private const string TABLE_APPLICATIONS_ACTIVITYFLOW = "t_Applications_ActivityFlow"; - private const string TABLE_TIERS_ACTIVITYFLOW = "t_Tiers_ActivityFlow"; - private const string TABLE_NODES_ACTIVITYFLOW = "t_Nodes_ActivityFlow"; - private const string TABLE_BACKENDS_ACTIVITYFLOW = "t_Backends_ActivityFlow"; - private const string TABLE_BUSINESS_TRANSACTIONS_ACTIVITYFLOW = "t_BusinessTransactions_ActivityFlow"; - private const string PIVOT_APPLICATIONS = "p_Applications"; private const string PIVOT_TIERS = "p_Tiers"; private const string PIVOT_TIERS_AVAILABILITY = "p_TiersAvailability"; @@ -135,6 +123,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_APM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + return true; } @@ -190,9 +181,6 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sheet.Cells[2, 1].Value = "See Pivot"; sheet.Cells[2, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_APPLICATIONS_PERF_PIVOT); sheet.Cells[2, 2].StyleName = "HyperLinkStyle"; - sheet.Cells[3, 1].Value = "See Activity Flow"; - sheet.Cells[3, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_APPLICATIONS_ACTIVITYFLOW); - sheet.Cells[3, 2].StyleName = "HyperLinkStyle"; sheet.View.FreezePanes(LIST_SHEET_START_TABLE_AT + 1, 1); sheet = excelReport.Workbook.Worksheets.Add(SHEET_APPLICATIONS_HOURLY); @@ -213,15 +201,6 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sheet.Cells[2, 2].StyleName = "HyperLinkStyle"; sheet.View.FreezePanes(PIVOT_SHEET_START_PIVOT_AT + 3, 1); - sheet = excelReport.Workbook.Worksheets.Add(SHEET_APPLICATIONS_ACTIVITYFLOW); - sheet.Cells[1, 1].Value = "Table of Contents"; - sheet.Cells[1, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TOC); - sheet.Cells[1, 2].StyleName = "HyperLinkStyle"; - sheet.Cells[2, 1].Value = "See Table"; - sheet.Cells[2, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_APPLICATIONS_FULL); - sheet.Cells[2, 2].StyleName = "HyperLinkStyle"; - sheet.View.FreezePanes(LIST_SHEET_START_TABLE_AT - 13 + 1, 1); - sheet = excelReport.Workbook.Worksheets.Add(SHEET_TIERS_FULL); sheet.Cells[1, 1].Value = "Table of Contents"; sheet.Cells[1, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TOC); @@ -229,12 +208,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sheet.Cells[2, 1].Value = "See Pivot"; sheet.Cells[2, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TIERS_PERF_PIVOT); sheet.Cells[2, 2].StyleName = "HyperLinkStyle"; - sheet.Cells[3, 1].Value = "See Activity Flow"; - sheet.Cells[3, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TIERS_ACTIVITYFLOW); + sheet.Cells[3, 1].Value = "See Agent Availability"; + sheet.Cells[3, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TIERS_AVAILABILITY_PIVOT); sheet.Cells[3, 2].StyleName = "HyperLinkStyle"; - sheet.Cells[4, 1].Value = "See Agent Availability"; - sheet.Cells[4, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TIERS_AVAILABILITY_PIVOT); - sheet.Cells[4, 2].StyleName = "HyperLinkStyle"; sheet.View.FreezePanes(LIST_SHEET_START_TABLE_AT + 1, 1); sheet = excelReport.Workbook.Worksheets.Add(SHEET_TIERS_HOURLY); @@ -244,12 +220,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sheet.Cells[2, 1].Value = "See Pivot"; sheet.Cells[2, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TIERS_PERF_PIVOT); sheet.Cells[2, 2].StyleName = "HyperLinkStyle"; - sheet.Cells[3, 1].Value = "See Activity Flow"; - sheet.Cells[3, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TIERS_ACTIVITYFLOW); + sheet.Cells[3, 1].Value = "See Agent Availability"; + sheet.Cells[3, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TIERS_AVAILABILITY_PIVOT); sheet.Cells[3, 2].StyleName = "HyperLinkStyle"; - sheet.Cells[4, 1].Value = "See Agent Availability"; - sheet.Cells[4, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TIERS_AVAILABILITY_PIVOT); - sheet.Cells[4, 2].StyleName = "HyperLinkStyle"; sheet.View.FreezePanes(LIST_SHEET_START_TABLE_AT + 1, 1); sheet = excelReport.Workbook.Worksheets.Add(SHEET_TIERS_PERF_PIVOT); @@ -270,15 +243,6 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sheet.Cells[2, 2].StyleName = "HyperLinkStyle"; sheet.View.FreezePanes(PIVOT_SHEET_START_PIVOT_AT + PIVOT_SHEET_CHART_HEIGHT + 5, 1); - sheet = excelReport.Workbook.Worksheets.Add(SHEET_TIERS_ACTIVITYFLOW); - sheet.Cells[1, 1].Value = "Table of Contents"; - sheet.Cells[1, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TOC); - sheet.Cells[1, 2].StyleName = "HyperLinkStyle"; - sheet.Cells[2, 1].Value = "See Table"; - sheet.Cells[2, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TIERS_FULL); - sheet.Cells[2, 2].StyleName = "HyperLinkStyle"; - sheet.View.FreezePanes(LIST_SHEET_START_TABLE_AT - 13 + 1, 1); - sheet = excelReport.Workbook.Worksheets.Add(SHEET_NODES_FULL); sheet.Cells[1, 1].Value = "Table of Contents"; sheet.Cells[1, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TOC); @@ -286,12 +250,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sheet.Cells[2, 1].Value = "See Pivot"; sheet.Cells[2, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_NODES_PERF_PIVOT); sheet.Cells[2, 2].StyleName = "HyperLinkStyle"; - sheet.Cells[3, 1].Value = "See Activity Flow"; - sheet.Cells[3, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_NODES_ACTIVITYFLOW); + sheet.Cells[3, 1].Value = "See Agent Availability"; + sheet.Cells[3, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_NODES_AVAILABILITY_PIVOT); sheet.Cells[3, 2].StyleName = "HyperLinkStyle"; - sheet.Cells[4, 1].Value = "See Agent Availability"; - sheet.Cells[4, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_NODES_AVAILABILITY_PIVOT); - sheet.Cells[4, 2].StyleName = "HyperLinkStyle"; sheet.View.FreezePanes(LIST_SHEET_START_TABLE_AT + 1, 1); sheet = excelReport.Workbook.Worksheets.Add(SHEET_NODES_HOURLY); @@ -301,12 +262,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sheet.Cells[2, 1].Value = "See Pivot"; sheet.Cells[2, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_NODES_PERF_PIVOT); sheet.Cells[2, 2].StyleName = "HyperLinkStyle"; - sheet.Cells[3, 1].Value = "See Activity Flow"; - sheet.Cells[3, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_NODES_ACTIVITYFLOW); + sheet.Cells[3, 1].Value = "See Agent Availability"; + sheet.Cells[3, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_NODES_AVAILABILITY_PIVOT); sheet.Cells[3, 2].StyleName = "HyperLinkStyle"; - sheet.Cells[4, 1].Value = "See Agent Availability"; - sheet.Cells[4, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_NODES_AVAILABILITY_PIVOT); - sheet.Cells[4, 2].StyleName = "HyperLinkStyle"; sheet.View.FreezePanes(LIST_SHEET_START_TABLE_AT + 1, 1); sheet = excelReport.Workbook.Worksheets.Add(SHEET_NODES_PERF_PIVOT); @@ -327,15 +285,6 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sheet.Cells[2, 2].StyleName = "HyperLinkStyle"; sheet.View.FreezePanes(PIVOT_SHEET_START_PIVOT_AT + PIVOT_SHEET_CHART_HEIGHT + 7, 1); - sheet = excelReport.Workbook.Worksheets.Add(SHEET_NODES_ACTIVITYFLOW); - sheet.Cells[1, 1].Value = "Table of Contents"; - sheet.Cells[1, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TOC); - sheet.Cells[1, 2].StyleName = "HyperLinkStyle"; - sheet.Cells[2, 1].Value = "See Table"; - sheet.Cells[2, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_NODES_FULL); - sheet.Cells[2, 2].StyleName = "HyperLinkStyle"; - sheet.View.FreezePanes(LIST_SHEET_START_TABLE_AT - 13 + 1, 1); - sheet = excelReport.Workbook.Worksheets.Add(SHEET_BACKENDS_FULL); sheet.Cells[1, 1].Value = "Table of Contents"; sheet.Cells[1, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TOC); @@ -343,9 +292,6 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sheet.Cells[2, 1].Value = "See Pivot"; sheet.Cells[2, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_BACKENDS_PERF_PIVOT); sheet.Cells[2, 2].StyleName = "HyperLinkStyle"; - sheet.Cells[3, 1].Value = "See Activity Flow"; - sheet.Cells[3, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_BACKENDS_ACTIVITYFLOW); - sheet.Cells[3, 2].StyleName = "HyperLinkStyle"; sheet.View.FreezePanes(LIST_SHEET_START_TABLE_AT + 1, 1); sheet = excelReport.Workbook.Worksheets.Add(SHEET_BACKENDS_HOURLY); @@ -366,15 +312,6 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sheet.Cells[2, 2].StyleName = "HyperLinkStyle"; sheet.View.FreezePanes(PIVOT_SHEET_START_PIVOT_AT + 3, 1); - sheet = excelReport.Workbook.Worksheets.Add(SHEET_BACKENDS_ACTIVITYFLOW); - sheet.Cells[1, 1].Value = "Table of Contents"; - sheet.Cells[1, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TOC); - sheet.Cells[1, 2].StyleName = "HyperLinkStyle"; - sheet.Cells[2, 1].Value = "See Table"; - sheet.Cells[2, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_BACKENDS_FULL); - sheet.Cells[2, 2].StyleName = "HyperLinkStyle"; - sheet.View.FreezePanes(LIST_SHEET_START_TABLE_AT - 13 + 1, 1); - sheet = excelReport.Workbook.Worksheets.Add(SHEET_BUSINESS_TRANSACTIONS_FULL); sheet.Cells[1, 1].Value = "Table of Contents"; sheet.Cells[1, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TOC); @@ -382,9 +319,6 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sheet.Cells[2, 1].Value = "See Pivot"; sheet.Cells[2, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_BUSINESS_TRANSACTIONS_PERF_PIVOT); sheet.Cells[2, 2].StyleName = "HyperLinkStyle"; - sheet.Cells[3, 1].Value = "See Activity Flow"; - sheet.Cells[3, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_BUSINESS_TRANSACTIONS_ACTIVITYFLOW); - sheet.Cells[3, 2].StyleName = "HyperLinkStyle"; sheet.View.FreezePanes(LIST_SHEET_START_TABLE_AT + 1, 1); sheet = excelReport.Workbook.Worksheets.Add(SHEET_BUSINESS_TRANSACTIONS_HOURLY); @@ -405,15 +339,6 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sheet.Cells[2, 2].StyleName = "HyperLinkStyle"; sheet.View.FreezePanes(PIVOT_SHEET_START_PIVOT_AT + 3, 1); - sheet = excelReport.Workbook.Worksheets.Add(SHEET_BUSINESS_TRANSACTIONS_ACTIVITYFLOW); - sheet.Cells[1, 1].Value = "Table of Contents"; - sheet.Cells[1, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TOC); - sheet.Cells[1, 2].StyleName = "HyperLinkStyle"; - sheet.Cells[2, 1].Value = "See Table"; - sheet.Cells[2, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_BUSINESS_TRANSACTIONS_FULL); - sheet.Cells[2, 2].StyleName = "HyperLinkStyle"; - sheet.View.FreezePanes(LIST_SHEET_START_TABLE_AT - 13 + 1, 1); - sheet = excelReport.Workbook.Worksheets.Add(SHEET_SERVICE_ENDPOINTS_FULL); sheet.Cells[1, 1].Value = "Table of Contents"; sheet.Cells[1, 2].Formula = String.Format(@"=HYPERLINK(""#'{0}'!A1"", """")", SHEET_TOC); @@ -527,11 +452,6 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sheet = excelReport.Workbook.Worksheets[SHEET_APPLICATIONS_HOURLY]; EPPlusCSVHelper.ReadCSVFileIntoExcelRange(FilePathMap.EntitiesHourReportFilePath(APMApplication.ENTITY_FOLDER), 0, sheet, LIST_SHEET_START_TABLE_AT, 1); - loggerConsole.Info("Applications Flowmap"); - - sheet = excelReport.Workbook.Worksheets[SHEET_APPLICATIONS_ACTIVITYFLOW]; - EPPlusCSVHelper.ReadCSVFileIntoExcelRange(FilePathMap.ApplicationsFlowmapReportFilePath(), 0, sheet, LIST_SHEET_START_TABLE_AT - 13, 1); - #endregion #region Tiers @@ -546,11 +466,6 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sheet = excelReport.Workbook.Worksheets[SHEET_TIERS_HOURLY]; EPPlusCSVHelper.ReadCSVFileIntoExcelRange(FilePathMap.EntitiesHourReportFilePath(APMTier.ENTITY_FOLDER), 0, sheet, LIST_SHEET_START_TABLE_AT, 1); - loggerConsole.Info("Tiers Flowmap"); - - sheet = excelReport.Workbook.Worksheets[SHEET_TIERS_ACTIVITYFLOW]; - EPPlusCSVHelper.ReadCSVFileIntoExcelRange(FilePathMap.TiersFlowmapReportFilePath(), 0, sheet, LIST_SHEET_START_TABLE_AT - 13, 1); - #endregion #region Nodes @@ -565,11 +480,6 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sheet = excelReport.Workbook.Worksheets[SHEET_NODES_HOURLY]; EPPlusCSVHelper.ReadCSVFileIntoExcelRange(FilePathMap.EntitiesHourReportFilePath(APMNode.ENTITY_FOLDER), 0, sheet, LIST_SHEET_START_TABLE_AT, 1); - loggerConsole.Info("Nodes Flowmap"); - - sheet = excelReport.Workbook.Worksheets[SHEET_NODES_ACTIVITYFLOW]; - EPPlusCSVHelper.ReadCSVFileIntoExcelRange(FilePathMap.NodesFlowmapReportFilePath(), 0, sheet, LIST_SHEET_START_TABLE_AT - 13, 1); - #endregion #region Backends @@ -584,11 +494,6 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sheet = excelReport.Workbook.Worksheets[SHEET_BACKENDS_HOURLY]; EPPlusCSVHelper.ReadCSVFileIntoExcelRange(FilePathMap.EntitiesHourReportFilePath(APMBackend.ENTITY_FOLDER), 0, sheet, LIST_SHEET_START_TABLE_AT, 1); - loggerConsole.Info("Backends Flowmap"); - - sheet = excelReport.Workbook.Worksheets[SHEET_BACKENDS_ACTIVITYFLOW]; - EPPlusCSVHelper.ReadCSVFileIntoExcelRange(FilePathMap.BackendsFlowmapReportFilePath(), 0, sheet, LIST_SHEET_START_TABLE_AT - 13, 1); - #endregion #region Business Transactions @@ -603,11 +508,6 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sheet = excelReport.Workbook.Worksheets[SHEET_BUSINESS_TRANSACTIONS_HOURLY]; EPPlusCSVHelper.ReadCSVFileIntoExcelRange(FilePathMap.EntitiesHourReportFilePath(APMBusinessTransaction.ENTITY_FOLDER), 0, sheet, LIST_SHEET_START_TABLE_AT, 1); - loggerConsole.Info("Business Transactions Flowmap"); - - sheet = excelReport.Workbook.Worksheets[SHEET_BUSINESS_TRANSACTIONS_ACTIVITYFLOW]; - EPPlusCSVHelper.ReadCSVFileIntoExcelRange(FilePathMap.BusinessTransactionsFlowmapReportFilePath(), 0, sheet, LIST_SHEET_START_TABLE_AT - 13, 1); - #endregion #region Service Endpoints @@ -736,21 +636,6 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sheet.Column(2).Width = 20; } - sheet = excelReport.Workbook.Worksheets[SHEET_APPLICATIONS_ACTIVITYFLOW]; - logger.Info("{0} Sheet ({1} rows)", sheet.Name, sheet.Dimension.Rows); - loggerConsole.Info("{0} Sheet ({1} rows)", sheet.Name, sheet.Dimension.Rows); - if (sheet.Dimension.Rows > LIST_SHEET_START_TABLE_AT - 13) - { - range = sheet.Cells[LIST_SHEET_START_TABLE_AT - 13, 1, sheet.Dimension.Rows, sheet.Dimension.Columns]; - table = sheet.Tables.Add(range, TABLE_APPLICATIONS_ACTIVITYFLOW); - table.ShowHeader = true; - table.TableStyle = TableStyles.Medium2; - table.ShowFilter = true; - table.ShowTotal = false; - - adjustColumnsOfActivityFlowRowTableInMetricReport(APMApplication.ENTITY_TYPE, sheet, table); - } - #endregion #region Tiers @@ -829,7 +714,7 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job addFilterFieldToPivot(pivot, "AvailAgent"); addFilterFieldToPivot(pivot, "AgentType"); ExcelPivotTableField fieldR = pivot.RowFields.Add(pivot.Fields["From"]); - fieldR.AddDateGrouping(eDateGroupBy.Days | eDateGroupBy.Hours); + fieldR.AddDateGrouping(eDateGroupBy.Months | eDateGroupBy.Days | eDateGroupBy.Hours); fieldR.Compact = false; fieldR.Outline = false; addColumnFieldToPivot(pivot, "Controller", eSortType.Ascending); @@ -845,21 +730,6 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sheet.Column(2).Width = 20; } - sheet = excelReport.Workbook.Worksheets[SHEET_TIERS_ACTIVITYFLOW]; - logger.Info("{0} Sheet ({1} rows)", sheet.Name, sheet.Dimension.Rows); - loggerConsole.Info("{0} Sheet ({1} rows)", sheet.Name, sheet.Dimension.Rows); - if (sheet.Dimension.Rows > LIST_SHEET_START_TABLE_AT - 13) - { - range = sheet.Cells[LIST_SHEET_START_TABLE_AT - 13, 1, sheet.Dimension.Rows, sheet.Dimension.Columns]; - table = sheet.Tables.Add(range, TABLE_TIERS_ACTIVITYFLOW); - table.ShowHeader = true; - table.TableStyle = TableStyles.Medium2; - table.ShowFilter = true; - table.ShowTotal = false; - - adjustColumnsOfActivityFlowRowTableInMetricReport(APMTier.ENTITY_TYPE, sheet, table); - } - #endregion #region Nodes @@ -939,17 +809,16 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job setDefaultPivotTableSettings(pivot); addFilterFieldToPivot(pivot, "IsAPMAgentUsed"); addFilterFieldToPivot(pivot, "IsMachineAgentUsed"); - addFilterFieldToPivot(pivot, "AvailAgent"); addFilterFieldToPivot(pivot, "AvailMachine"); addFilterFieldToPivot(pivot, "AgentType"); ExcelPivotTableField fieldR = pivot.RowFields.Add(pivot.Fields["From"]); - fieldR.AddDateGrouping(eDateGroupBy.Days | eDateGroupBy.Hours); + fieldR.AddDateGrouping(eDateGroupBy.Months | eDateGroupBy.Days | eDateGroupBy.Hours); fieldR.Compact = false; fieldR.Outline = false; addColumnFieldToPivot(pivot, "Controller", eSortType.Ascending); addColumnFieldToPivot(pivot, "ApplicationName", eSortType.Ascending); addColumnFieldToPivot(pivot, "TierName", eSortType.Ascending); - addDataFieldToPivot(pivot, "NodeID", DataFieldFunctions.Count); + addDataFieldToPivot(pivot, "AvailAgent", DataFieldFunctions.Average); ExcelChart chart = sheet.Drawings.AddChart(GRAPH_NODES_AVAILABILITY, eChartType.Line, pivot); chart.SetPosition(2, 0, 0, 0); @@ -959,21 +828,6 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sheet.Column(2).Width = 20; } - sheet = excelReport.Workbook.Worksheets[SHEET_NODES_ACTIVITYFLOW]; - logger.Info("{0} Sheet ({1} rows)", sheet.Name, sheet.Dimension.Rows); - loggerConsole.Info("{0} Sheet ({1} rows)", sheet.Name, sheet.Dimension.Rows); - if (sheet.Dimension.Rows > LIST_SHEET_START_TABLE_AT - 13) - { - range = sheet.Cells[LIST_SHEET_START_TABLE_AT - 13, 1, sheet.Dimension.Rows, sheet.Dimension.Columns]; - table = sheet.Tables.Add(range, TABLE_NODES_ACTIVITYFLOW); - table.ShowHeader = true; - table.TableStyle = TableStyles.Medium2; - table.ShowFilter = true; - table.ShowTotal = false; - - adjustColumnsOfActivityFlowRowTableInMetricReport(APMNode.ENTITY_TYPE, sheet, table); - } - #endregion #region Backends @@ -1043,21 +897,6 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sheet.Column(4).Width = 20; } - sheet = excelReport.Workbook.Worksheets[SHEET_BACKENDS_ACTIVITYFLOW]; - logger.Info("{0} Sheet ({1} rows)", sheet.Name, sheet.Dimension.Rows); - loggerConsole.Info("{0} Sheet ({1} rows)", sheet.Name, sheet.Dimension.Rows); - if (sheet.Dimension.Rows > LIST_SHEET_START_TABLE_AT - 13) - { - range = sheet.Cells[LIST_SHEET_START_TABLE_AT - 13, 1, sheet.Dimension.Rows, sheet.Dimension.Columns]; - table = sheet.Tables.Add(range, TABLE_BACKENDS_ACTIVITYFLOW); - table.ShowHeader = true; - table.TableStyle = TableStyles.Medium2; - table.ShowFilter = true; - table.ShowTotal = false; - - adjustColumnsOfActivityFlowRowTableInMetricReport(APMBackend.ENTITY_TYPE, sheet, table); - } - #endregion #region Business Transactions @@ -1129,21 +968,6 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sheet.Column(5).Width = 20; } - sheet = excelReport.Workbook.Worksheets[SHEET_BUSINESS_TRANSACTIONS_ACTIVITYFLOW]; - logger.Info("{0} Sheet ({1} rows)", sheet.Name, sheet.Dimension.Rows); - loggerConsole.Info("{0} Sheet ({1} rows)", sheet.Name, sheet.Dimension.Rows); - if (sheet.Dimension.Rows > LIST_SHEET_START_TABLE_AT - 13) - { - range = sheet.Cells[LIST_SHEET_START_TABLE_AT - 13, 1, sheet.Dimension.Rows, sheet.Dimension.Columns]; - table = sheet.Tables.Add(range, TABLE_BUSINESS_TRANSACTIONS_ACTIVITYFLOW); - table.ShowHeader = true; - table.TableStyle = TableStyles.Medium2; - table.ShowFilter = true; - table.ShowTotal = false; - - adjustColumnsOfActivityFlowRowTableInMetricReport(APMBusinessTransaction.ENTITY_TYPE, sheet, table); - } - #endregion #region Service Endpoints @@ -1409,15 +1233,13 @@ public override bool ShouldExecute(ProgramOptions programOptions, JobConfigurati logger.Trace("Input.Metrics={0}", jobConfiguration.Input.Metrics); loggerConsole.Trace("Input.Metrics={0}", jobConfiguration.Input.Metrics); - logger.Trace("Input.Flowmaps={0}", jobConfiguration.Input.Flowmaps); - loggerConsole.Trace("Input.Flowmaps={0}", jobConfiguration.Input.Flowmaps); logger.Trace("Output.EntityMetrics={0}", jobConfiguration.Output.EntityMetrics); loggerConsole.Trace("Output.EntityMetrics={0}", jobConfiguration.Output.EntityMetrics); - if ((jobConfiguration.Input.Metrics == false && jobConfiguration.Input.Flowmaps == false) || jobConfiguration.Output.EntityMetrics == false) + if (jobConfiguration.Input.Metrics == false && jobConfiguration.Output.EntityMetrics == false) { loggerConsole.Trace("Skipping report of entity metrics"); } - return ((jobConfiguration.Input.Metrics == true || jobConfiguration.Input.Flowmaps == true) && jobConfiguration.Output.EntityMetrics == true); + return (jobConfiguration.Input.Flowmaps == true && jobConfiguration.Output.EntityMetrics == true); } private static void addConditionalFormattingToTableInMetricReport(string entityType, ExcelWorksheet sheet, ExcelTable table) diff --git a/ProcessingSteps/Report/ReportAPMSnapshots.cs b/ProcessingSteps/Report/ReportAPMSnapshots.cs index c2aa29a..7ae4247 100644 --- a/ProcessingSteps/Report/ReportAPMSnapshots.cs +++ b/ProcessingSteps/Report/ReportAPMSnapshots.cs @@ -114,6 +114,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_APM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + return true; } diff --git a/ProcessingSteps/Report/ReportAPMSnapshotsMethodCallLines.cs b/ProcessingSteps/Report/ReportAPMSnapshotsMethodCallLines.cs index 42cf479..4c1f13d 100644 --- a/ProcessingSteps/Report/ReportAPMSnapshotsMethodCallLines.cs +++ b/ProcessingSteps/Report/ReportAPMSnapshotsMethodCallLines.cs @@ -68,6 +68,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_APM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_APM); + return true; } diff --git a/ProcessingSteps/Report/ReportBIQEntities.cs b/ProcessingSteps/Report/ReportBIQEntities.cs index 29cf2a6..864333c 100644 --- a/ProcessingSteps/Report/ReportBIQEntities.cs +++ b/ProcessingSteps/Report/ReportBIQEntities.cs @@ -89,6 +89,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_BIQ) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_BIQ); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_BIQ); + return true; } diff --git a/ProcessingSteps/Report/ReportDBEntities.cs b/ProcessingSteps/Report/ReportDBEntities.cs index a4eaf33..9ddbc8a 100644 --- a/ProcessingSteps/Report/ReportDBEntities.cs +++ b/ProcessingSteps/Report/ReportDBEntities.cs @@ -108,6 +108,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_DB) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_DB); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_DB); + return true; } diff --git a/ProcessingSteps/Report/ReportHealthCheck.cs b/ProcessingSteps/Report/ReportHealthCheck.cs index 7ed2c5f..b6283f4 100644 --- a/ProcessingSteps/Report/ReportHealthCheck.cs +++ b/ProcessingSteps/Report/ReportHealthCheck.cs @@ -436,18 +436,21 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job // Make header row taller sheet.Row(rowTableStart).Height = 40; - // Color code it - ExcelAddress cfGradeNum = new ExcelAddress(LIST_SHEET_START_TABLE_AT + 1, 4, sheet.Dimension.Rows, 4 + numColumns); - var cfGrade = sheet.ConditionalFormatting.AddThreeColorScale(cfGradeNum); - cfGrade.LowValue.Type = eExcelConditionalFormattingValueObjectType.Num; - cfGrade.LowValue.Color = colorRedFor3ColorScales; - cfGrade.LowValue.Value = 1; - cfGrade.MiddleValue.Type = eExcelConditionalFormattingValueObjectType.Num; - cfGrade.MiddleValue.Value = 3; - cfGrade.MiddleValue.Color = colorYellowFor3ColorScales; - cfGrade.HighValue.Type = eExcelConditionalFormattingValueObjectType.Num; - cfGrade.HighValue.Color = colorGreenFor3ColorScales; - cfGrade.HighValue.Value = 5; + if (sheet.Dimension.Rows > rowTableStart) + { + // Color code it + ExcelAddress cfGradeNum = new ExcelAddress(LIST_SHEET_START_TABLE_AT + 1, 4, sheet.Dimension.Rows, 4 + numColumns); + var cfGrade = sheet.ConditionalFormatting.AddThreeColorScale(cfGradeNum); + cfGrade.LowValue.Type = eExcelConditionalFormattingValueObjectType.Num; + cfGrade.LowValue.Color = colorRedFor3ColorScales; + cfGrade.LowValue.Value = 1; + cfGrade.MiddleValue.Type = eExcelConditionalFormattingValueObjectType.Num; + cfGrade.MiddleValue.Value = 3; + cfGrade.MiddleValue.Color = colorYellowFor3ColorScales; + cfGrade.HighValue.Type = eExcelConditionalFormattingValueObjectType.Num; + cfGrade.HighValue.Color = colorGreenFor3ColorScales; + cfGrade.HighValue.Value = 5; + } #endregion @@ -568,6 +571,14 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job sb.AppendFormat("{0}: {1}\n", k + 1, wordWrapString(healthCheckRuleResult.Description, 100)); } + // Limit the size of the comment generated to ~2K of text because I think the Excel barfs when the comments are too long. + if (sb.Length > 2500) + { + sb.Length = 2547; + sb.Append("..."); + } + + // Excessive comments in the workbook lead to poor use experience. Need to rethink and refactor this ExcelComment comment = sheet.Cells[fromRow + rowIndex, gradeColumnStart + columnIndex].AddComment(sb.ToString(), healthCheckRuleResultsInCell[0].Code); comment.AutoFit = true; } @@ -603,18 +614,21 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job // Make header row taller sheet.Row(rowTableStart).Height = 50; - // Color code it - cfGradeNum = new ExcelAddress(LIST_SHEET_START_TABLE_AT + 1, 5, sheet.Dimension.Rows, 5 + numColumns); - cfGrade = sheet.ConditionalFormatting.AddThreeColorScale(cfGradeNum); - cfGrade.LowValue.Type = eExcelConditionalFormattingValueObjectType.Num; - cfGrade.LowValue.Color = colorRedFor3ColorScales; - cfGrade.LowValue.Value = 1; - cfGrade.MiddleValue.Type = eExcelConditionalFormattingValueObjectType.Num; - cfGrade.MiddleValue.Value = 3; - cfGrade.MiddleValue.Color = colorYellowFor3ColorScales; - cfGrade.HighValue.Type = eExcelConditionalFormattingValueObjectType.Num; - cfGrade.HighValue.Color = colorGreenFor3ColorScales; - cfGrade.HighValue.Value = 5; + if (sheet.Dimension.Rows > rowTableStart) + { + // Color code it + ExcelAddress cfGradeNum = new ExcelAddress(LIST_SHEET_START_TABLE_AT + 1, 5, sheet.Dimension.Rows, 5 + numColumns); + var cfGrade = sheet.ConditionalFormatting.AddThreeColorScale(cfGradeNum); + cfGrade.LowValue.Type = eExcelConditionalFormattingValueObjectType.Num; + cfGrade.LowValue.Color = colorRedFor3ColorScales; + cfGrade.LowValue.Value = 1; + cfGrade.MiddleValue.Type = eExcelConditionalFormattingValueObjectType.Num; + cfGrade.MiddleValue.Value = 3; + cfGrade.MiddleValue.Color = colorYellowFor3ColorScales; + cfGrade.HighValue.Type = eExcelConditionalFormattingValueObjectType.Num; + cfGrade.HighValue.Color = colorGreenFor3ColorScales; + cfGrade.HighValue.Value = 5; + } } #endregion diff --git a/ProcessingSteps/Report/ReportMOBILEEntities.cs b/ProcessingSteps/Report/ReportMOBILEEntities.cs index cc95187..05dfb15 100644 --- a/ProcessingSteps/Report/ReportMOBILEEntities.cs +++ b/ProcessingSteps/Report/ReportMOBILEEntities.cs @@ -67,6 +67,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_MOBILE) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_MOBILE); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_MOBILE); + return true; } diff --git a/ProcessingSteps/Report/ReportSIMEntities.cs b/ProcessingSteps/Report/ReportSIMEntities.cs index e826da9..1769b0d 100644 --- a/ProcessingSteps/Report/ReportSIMEntities.cs +++ b/ProcessingSteps/Report/ReportSIMEntities.cs @@ -93,6 +93,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_SIM) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_SIM); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_SIM); + return true; } diff --git a/ProcessingSteps/Report/ReportWEBEntities.cs b/ProcessingSteps/Report/ReportWEBEntities.cs index c93f920..f5ccf13 100644 --- a/ProcessingSteps/Report/ReportWEBEntities.cs +++ b/ProcessingSteps/Report/ReportWEBEntities.cs @@ -77,6 +77,9 @@ public override bool Execute(ProgramOptions programOptions, JobConfiguration job if (jobConfiguration.Target.Count(t => t.Type == APPLICATION_TYPE_WEB) == 0) { + logger.Warn("No {0} targets to process", APPLICATION_TYPE_WEB); + loggerConsole.Warn("No {0} targets to process", APPLICATION_TYPE_WEB); + return true; } diff --git a/Program.cs b/Program.cs index eb744d5..ab53cf1 100644 --- a/Program.cs +++ b/Program.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Drawing; using System.Globalization; using System.IO; using System.Linq; @@ -16,6 +17,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading; +using System.Xml; namespace AppDynamics.Dexter { @@ -33,7 +35,6 @@ public static void Main(string[] args) { logger.Trace("AppDynamics DEXTER Version {0}", Assembly.GetEntryAssembly().GetName().Version); loggerConsole.Info("AppDynamics DEXTER Version {0}", Assembly.GetEntryAssembly().GetName().Version); - Console.WriteLine(); logger.Trace("Starting at local {0:o}/UTC {1:o}, Version={2}, Parameters={3}", DateTime.Now, DateTime.UtcNow, Assembly.GetEntryAssembly().GetName().Version, String.Join(" ", args)); logger.Trace("Timezone {0} {1} {2}", TimeZoneInfo.Local.DisplayName, TimeZoneInfo.Local.StandardName, TimeZoneInfo.Local.BaseUtcOffset); logger.Trace("Culture {0}({1}), ShortDate={2}, ShortTime={3}, All={4}", CultureInfo.CurrentCulture.DisplayName, CultureInfo.CurrentCulture.Name, CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern, CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern, String.Join(";", CultureInfo.CurrentCulture.DateTimeFormat.GetAllDateTimePatterns())); @@ -71,36 +72,8 @@ public static void Main(string[] args) public static void RunProgram(ProgramOptions programOptions) { - // Choose type of program to run, ETL or Compare - - if (programOptions.InputJobFilePath != null && programOptions.InputJobFilePath.Length > 0) - { - RunProgramETL(programOptions); - } - else if (programOptions.CompareFilePath != null && programOptions.CompareFilePath.Length > 0) - { - RunProgramCompare(programOptions); - } - } - - public static void RunProgramETL(ProgramOptions programOptions) - { - #region Validate job file exists - - programOptions.InputJobFilePath = Path.GetFullPath(programOptions.InputJobFilePath); - - logger.Info("Checking input job file {0}", programOptions.InputJobFilePath); - loggerConsole.Info("Checking input job file {0}", programOptions.InputJobFilePath); - - if (File.Exists(programOptions.InputJobFilePath) == false) - { - logger.Error("Job file {0} does not exist", programOptions.InputJobFilePath); - loggerConsole.Error("Job file {0} does not exist", programOptions.InputJobFilePath); - - return; - } - - #endregion + logger.Trace("Executing:\r\n{0}", programOptions); + loggerConsole.Trace("Executing:\r\n{0}", programOptions); #region Check version and prompt someone @@ -155,365 +128,952 @@ public static void RunProgramETL(ProgramOptions programOptions) #endregion - #region Create output folder + // Choose type of program to run, ETL or Compare + if (programOptions.InputETLJobFilePath != null && programOptions.InputETLJobFilePath.Length > 0) + { + loggerConsole.Info("Running ETL workload"); - // If output folder isn't specified, get default output folder - if (programOptions.OutputFolderPath == null || programOptions.OutputFolderPath.Length == 0) + RunProgramETL(programOptions); + } + else if (programOptions.InputCompareJobFilePath != null && programOptions.InputCompareJobFilePath.Length > 0) { - // Windows: at the root of C: on Windows - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) == true) - { - programOptions.OutputFolderPath = @"C:\AppD.Dexter.Out"; - } - // Mac/Linux: a child of %HOME% path - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) == true || RuntimeInformation.IsOSPlatform(OSPlatform.OSX) == true) - { - programOptions.OutputFolderPath = Path.Combine(Environment.GetEnvironmentVariable("HOME"), "AppD.Dexter.Out"); - } + loggerConsole.Info("Running Compare workload"); + + RunProgramCompare(programOptions); } + } - programOptions.OutputFolderPath = Path.GetFullPath(programOptions.OutputFolderPath); + [System.Diagnostics.CodeAnalysis.SuppressMessage("Compiler", "CS0168", Justification = "Hiding ArgumentOutOfRangeException and FormatException that may occur when dates")] + public static void RunProgramETL(ProgramOptions programOptions) + { + #region Validate job file exists and load it - logger.Info("Creating output folder {0}", programOptions.OutputFolderPath); - loggerConsole.Info("Creating output folder {0}", programOptions.OutputFolderPath); + programOptions.InputETLJobFilePath = Path.GetFullPath(programOptions.InputETLJobFilePath); - if (FileIOHelper.CreateFolder(programOptions.OutputFolderPath) == false) + logger.Info("Checking input ETL job file {0}", programOptions.InputETLJobFilePath); + loggerConsole.Info("Checking input ETL job file {0}", programOptions.InputETLJobFilePath); + + if (File.Exists(programOptions.InputETLJobFilePath) == false) { - logger.Error("Unable to create output folder={0}", programOptions.OutputFolderPath); - loggerConsole.Error("Unable to create output folder={0}", programOptions.OutputFolderPath); + logger.Error("ETL job file {0} does not exist", programOptions.InputETLJobFilePath); + loggerConsole.Error("ETL job file {0} does not exist", programOptions.InputETLJobFilePath); return; } + + // Load job configuration + JobConfiguration jobConfiguration = FileIOHelper.ReadJobConfigurationFromFile(programOptions.InputETLJobFilePath); + if (jobConfiguration == null) + { + logger.Error("Unable to load job input file {0}", programOptions.InputETLJobFilePath); + loggerConsole.Error("Unable to load job input file {0}", programOptions.InputETLJobFilePath); - #endregion + return; + } - #region Create job output folder + if (jobConfiguration.Input == null) + { + logger.Error("Job File Problem: Input can not be empty"); + loggerConsole.Error("Job File Problem: Input can not be empty"); - // Set up the output job folder and job file path - programOptions.JobName = Path.GetFileNameWithoutExtension(programOptions.InputJobFilePath); - programOptions.OutputJobFolderPath = Path.Combine(programOptions.OutputFolderPath, programOptions.JobName); - programOptions.OutputJobFilePath = Path.Combine(programOptions.OutputJobFolderPath, "jobparameters.json"); - programOptions.ProgramLocationFolderPath = AppDomain.CurrentDomain.BaseDirectory; + return; + } - // Create job folder if it doesn't exist - // or - // Clear out job folder if it already exists and restart of the job was requested - if (programOptions.RestartJobFromBeginning) + if (jobConfiguration.Output == null) { - if (FileIOHelper.DeleteFolder(programOptions.OutputJobFolderPath) == false) - { - logger.Error("Unable to clear job folder {0}", programOptions.OutputJobFolderPath); - loggerConsole.Error("Unable to clear job folder {0}", programOptions.OutputJobFolderPath); - - return; - } + logger.Error("Job File Problem: Output can not be empty"); + loggerConsole.Error("Job File Problem: Output can not be empty"); - // Sleep after deleting to let the file system catch up - Thread.Sleep(1000); + return; } - logger.Info("Creating job output folder {0}", programOptions.OutputJobFolderPath); - loggerConsole.Info("Creating job output folder {0}", programOptions.OutputJobFolderPath); - - if (FileIOHelper.CreateFolder(programOptions.OutputJobFolderPath) == false) + if (jobConfiguration.Target == null || jobConfiguration.Target.Count == 0) { - logger.Error("Unable to create job output folder={0}", programOptions.OutputJobFolderPath); - loggerConsole.Error("Unable to create job output folder={0}", programOptions.OutputJobFolderPath); + logger.Error("Job File Problem: No targets to work on"); + loggerConsole.Error("Job File Problem: No targets to work on"); return; } #endregion - #region Process input job file to output job file + #region Validate Input - Time Range - // Check if this job file was already already validated and exists in target folder - loggerConsole.Info("Processing input job file to output job file"); - if (Directory.Exists(programOptions.OutputJobFolderPath) == false || File.Exists(programOptions.OutputJobFilePath) == false) - { - // New job - // Validate job file for validity if the job is new - // Expand list of targets from the input file - // Save validated job file to the output directory + DateTime dateTimeNowOriginal = DateTime.Now; + DateTime dateTimeNowOriginalUtc = dateTimeNowOriginal.ToUniversalTime(); - // Load job configuration - JobConfiguration jobConfiguration = FileIOHelper.ReadJobConfigurationFromFile(programOptions.InputJobFilePath); - if (jobConfiguration == null) + // Validate input time range selection + if (jobConfiguration.Input.TimeFrame != null) + { + #region Validate new style TimeFrame + + // Using Timespan https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-timespan-format-strings?view=netcore-3.1#the-constant-c-format-specifier + //"TimeFrame": { + // "MarkDate": "2020-03-20", + // "MarkTime": "10:00:00", + // "Duration": "1:00:00" + //} + + // Or using ISO 8601 time interval https://en.wikipedia.org/wiki/ISO_8601#Time_intervals + // Going Forward in time from mark: + //"TimeFrame": { + // "MarkDate": "2020-03-20", + // "MarkTime": "10:00:00", + // "Duration": "PT1H" + //} + // Going Backward in time from mark: + //"TimeFrame": { + // "MarkDate": "2020-03-20", + // "MarkTime": "10:00:00", + // "Duration": "-PT1H" + //} + + // The newer type of relative time range specifier in newer job files + + if (jobConfiguration.Input.TimeFrame.MarkDate == null || jobConfiguration.Input.TimeFrame.MarkDate.Length == 0) { - loggerConsole.Error("Unable to load job input file {0}", programOptions.InputJobFilePath); + logger.Error("Job File Problem: Input.TimeFrame.MarkDate can not be empty"); + loggerConsole.Error("Job File Problem: Input.TimeFrame.MarkDate can not be empty"); return; } - - #region Validate Input - - if (jobConfiguration.Input == null) + if (jobConfiguration.Input.TimeFrame.MarkTime == null || jobConfiguration.Input.TimeFrame.MarkTime.Length == 0) { - logger.Error("Job File Problem: Input can not be empty"); - loggerConsole.Error("Job File Problem: Input can not be empty"); + logger.Error("Job File Problem: Input.TimeFrame.MarkTime can not be empty"); + loggerConsole.Error("Job File Problem: Input.TimeFrame.MarkTime can not be empty"); return; } - - // Validate input time range selection - if (jobConfiguration.Input.TimeRange == null || jobConfiguration.Input.TimeRange.From == null || jobConfiguration.Input.TimeRange.From == DateTime.MinValue) + if (jobConfiguration.Input.TimeFrame.Duration == null || jobConfiguration.Input.TimeFrame.Duration.Length == 0) { - logger.Error("Job File Problem: Input.TimeRange.From can not be empty"); - loggerConsole.Error("Job File Problem: Input.TimeRange.From can not be empty"); + logger.Error("Job File Problem: Input.TimeFrame.Duration can not be empty"); + loggerConsole.Error("Job File Problem: Input.TimeFrame.Duration can not be empty"); return; } - else if (jobConfiguration.Input.TimeRange == null || jobConfiguration.Input.TimeRange.To == null || jobConfiguration.Input.TimeRange.To == DateTime.MinValue) - { - logger.Error("Job File Problem: Input.TimeRange.To can not be empty"); - loggerConsole.Error("Job File Problem: Input.TimeRange.To can not be empty"); - return; + // Parse out date of the point from which we're counting + DateTime markDate = DateTime.MinValue; + if (DateTime.TryParse(jobConfiguration.Input.TimeFrame.MarkDate, null, DateTimeStyles.AssumeLocal, out markDate) == true) + { + //markDate = markDate.ToUniversalTime(); } + else + { + logger.Warn("Job File Problem: Input.TimeFrame.MarkDate={0} is not a valid Date", jobConfiguration.Input.TimeFrame.MarkDate); - jobConfiguration.Input.TimeRange.From = jobConfiguration.Input.TimeRange.From.ToUniversalTime(); - jobConfiguration.Input.TimeRange.To = jobConfiguration.Input.TimeRange.To.ToUniversalTime(); + DateTimeKind kind = dateTimeNowOriginal.Kind; + string token = jobConfiguration.Input.TimeFrame.MarkDate.ToUpper(); + if (jobConfiguration.Input.TimeFrame.MarkDate.ToUpper().EndsWith("_Z") == true) + { + kind = DateTimeKind.Utc; + token = token.Substring(0, token.Length - 2); + } - if (jobConfiguration.Input.TimeRange.From > jobConfiguration.Input.TimeRange.To) - { - logger.Error("Job File Problem: Input.TimeRange.From='{0:o}' can not be > Input.TimeRange.To='{1:o}'", jobConfiguration.Input.TimeRange.From, jobConfiguration.Input.TimeRange.To); - loggerConsole.Error("Job File Problem: Input.TimeRange.From='{0:o}' can not be > Input.TimeRange.To='{1:o}'", jobConfiguration.Input.TimeRange.From, jobConfiguration.Input.TimeRange.To); + // Let's try one of the tokens + switch (token) + { + #region Day offsets + + case "TODAY": + markDate = new DateTime( + dateTimeNowOriginal.Year, + dateTimeNowOriginal.Month, + dateTimeNowOriginal.Day, + 0, 0, 0, + kind); + + break; + + case "YESTERDAY": + markDate = new DateTime( + dateTimeNowOriginal.Year, + dateTimeNowOriginal.Month, + dateTimeNowOriginal.Day, + 0, 0, 0, + kind).AddDays(-1); + + break; + + case "DAY_BEFORE_YESTERDAY": + markDate = new DateTime( + dateTimeNowOriginal.Year, + dateTimeNowOriginal.Month, + dateTimeNowOriginal.Day, + 0, 0, 0, + kind).AddDays(-2); + + break; + + case "SAME_DAY_LAST_WEEK": + markDate = new DateTime( + dateTimeNowOriginal.Year, + dateTimeNowOriginal.Month, + dateTimeNowOriginal.Day, + 0, 0, 0, + kind).AddDays(-7); + + break; - return; - } - else if (jobConfiguration.Input.TimeRange.From > DateTime.UtcNow) - { - logger.Error("Job File Problem: Input.TimeRange.From='{0:o}' can not be in the future", jobConfiguration.Input.TimeRange.From); - loggerConsole.Error("Job File Problem: Input.TimeRange.From='{0:o}' can not be in the future", jobConfiguration.Input.TimeRange.From); + #endregion - return; - } - logger.Info("UTC Input.TimeRange.From='{0:o}' to Input.TimeRange.To='{1:o}'", jobConfiguration.Input.TimeRange.From, jobConfiguration.Input.TimeRange.To); - loggerConsole.Info("UTC Input.TimeRange.From='{0:o}' to Input.TimeRange.To='{1:o}'", jobConfiguration.Input.TimeRange.From, jobConfiguration.Input.TimeRange.To); - logger.Info("Local Input.TimeRange.From='{0:o}' to Input.TimeRange.To='{1:o}'", jobConfiguration.Input.TimeRange.From.ToLocalTime(), jobConfiguration.Input.TimeRange.To.ToLocalTime()); - loggerConsole.Info("Local Input.TimeRange.From='{0:o}' to Input.TimeRange.To='{1:o}'", jobConfiguration.Input.TimeRange.From.ToLocalTime(), jobConfiguration.Input.TimeRange.To.ToLocalTime()); + #region Weekdays - // Validate Metrics selection - if (jobConfiguration.Input.MetricsSelectionCriteria == null) - { - jobConfiguration.Input.MetricsSelectionCriteria = new string[1]; - jobConfiguration.Input.MetricsSelectionCriteria[0] = "TransactionApplication"; - } + case "MONDAY": + DateTime dateTime_Token_Monday = new DateTime( + dateTimeNowOriginal.Year, + dateTimeNowOriginal.Month, + dateTimeNowOriginal.Day, + 0, 0, 0, + kind); - // Validate Events selection - if (jobConfiguration.Input.EventsSelectionCriteria == null || jobConfiguration.Input.EventsSelectionCriteria.Length == 0) - { - jobConfiguration.Input.EventsSelectionCriteria = new string[1]; - jobConfiguration.Input.EventsSelectionCriteria[0] = "None"; - } + while (dateTime_Token_Monday.DayOfWeek != DayOfWeek.Monday) + { + dateTime_Token_Monday = dateTime_Token_Monday.AddDays(-1); + } + markDate = dateTime_Token_Monday; - // Validate Snapshot selection criteria - if (jobConfiguration.Input.SnapshotSelectionCriteria == null) - { - jobConfiguration.Input.SnapshotSelectionCriteria = new JobSnapshotSelectionCriteria(); - } + break; - if (jobConfiguration.Input.SnapshotSelectionCriteria.Tiers == null) - { - jobConfiguration.Input.SnapshotSelectionCriteria.Tiers = new string[0]; - } + case "TUESDAY": + DateTime dateTime_Token_Tuesday = new DateTime( + dateTimeNowOriginal.Year, + dateTimeNowOriginal.Month, + dateTimeNowOriginal.Day, + 0, 0, 0, + kind); - if (jobConfiguration.Input.SnapshotSelectionCriteria.BusinessTransactions == null) - { - jobConfiguration.Input.SnapshotSelectionCriteria.BusinessTransactions = new string[0]; - } + while (dateTime_Token_Tuesday.DayOfWeek != DayOfWeek.Tuesday) + { + dateTime_Token_Tuesday = dateTime_Token_Tuesday.AddDays(-1); + } + markDate = dateTime_Token_Tuesday; - if (jobConfiguration.Input.SnapshotSelectionCriteria.TierType == null) - { - jobConfiguration.Input.SnapshotSelectionCriteria.TierType = new JobTierType(); - jobConfiguration.Input.SnapshotSelectionCriteria.TierType.All = true; - } + break; - if (jobConfiguration.Input.SnapshotSelectionCriteria.BusinessTransactionType == null) - { - jobConfiguration.Input.SnapshotSelectionCriteria.BusinessTransactionType = new JobBusinessTransactionType(); - jobConfiguration.Input.SnapshotSelectionCriteria.BusinessTransactionType.All = true; + case "WEDNESDAY": + DateTime dateTime_Token_Wednesday = new DateTime( + dateTimeNowOriginal.Year, + dateTimeNowOriginal.Month, + dateTimeNowOriginal.Day, + 0, 0, 0, + kind); + + while (dateTime_Token_Wednesday.DayOfWeek != DayOfWeek.Wednesday) + { + dateTime_Token_Wednesday = dateTime_Token_Wednesday.AddDays(-1); + } + markDate = dateTime_Token_Wednesday; + + break; + + case "THURSDAY": + DateTime dateTime_Token_Thursday = new DateTime( + dateTimeNowOriginal.Year, + dateTimeNowOriginal.Month, + dateTimeNowOriginal.Day, + 0, 0, 0, + kind); + + while (dateTime_Token_Thursday.DayOfWeek != DayOfWeek.Thursday) + { + dateTime_Token_Thursday = dateTime_Token_Thursday.AddDays(-1); + } + markDate = dateTime_Token_Thursday; + + break; + + case "FRIDAY": + DateTime dateTime_Token_Friday = new DateTime( + dateTimeNowOriginal.Year, + dateTimeNowOriginal.Month, + dateTimeNowOriginal.Day, + 0, 0, 0, + kind); + + while (dateTime_Token_Friday.DayOfWeek != DayOfWeek.Friday) + { + dateTime_Token_Friday = dateTime_Token_Friday.AddDays(-1); + } + markDate = dateTime_Token_Friday; + + break; + + case "SATURDAY": + DateTime dateTime_Token_Saturday = new DateTime( + dateTimeNowOriginal.Year, + dateTimeNowOriginal.Month, + dateTimeNowOriginal.Day, + 0, 0, 0, + kind); + + while (dateTime_Token_Saturday.DayOfWeek != DayOfWeek.Saturday) + { + dateTime_Token_Saturday = dateTime_Token_Saturday.AddDays(-1); + } + markDate = dateTime_Token_Saturday; + + break; + + case "SUNDAY": + DateTime dateTime_Token_Sunday = new DateTime( + dateTimeNowOriginal.Year, + dateTimeNowOriginal.Month, + dateTimeNowOriginal.Day, + 0, 0, 0, + kind); + + while (dateTime_Token_Sunday.DayOfWeek != DayOfWeek.Sunday) + { + dateTime_Token_Sunday = dateTime_Token_Sunday.AddDays(-1); + } + markDate = dateTime_Token_Sunday; + + break; + + #endregion + + default: + // Day of Month? + if (token.StartsWith("DAY_OF_MONTH") == true) + { + string tokenWithoutZ = token.Replace("_Z", ""); + string dayOfMonthToken = tokenWithoutZ.Substring("DAY_OF_MONTH_".Length); + int dayOfMonth = -1; + if (Int32.TryParse(dayOfMonthToken, out dayOfMonth) == false) + { + logger.Error("Job File Problem: Input.TimeFrame.MarkDate={0} is not a valid day of month token", jobConfiguration.Input.TimeFrame.MarkDate); + } + else + { + if (dayOfMonth < 1 || dayOfMonth > 31) + { + logger.Error("Job File Problem: Input.TimeFrame.MarkDate={0} is not a valid day of month token", jobConfiguration.Input.TimeFrame.MarkDate); + } + else + { + // Got a day of the month in range of 1..31 + // The months are all different in size. Let's find that day. If we're in the month with 30 days and we are on day 31, we'll go to 30 + DateTime dateTime_Token_Today = new DateTime( + dateTimeNowOriginal.Year, + dateTimeNowOriginal.Month, + dateTimeNowOriginal.Day, + 0, 0, 0, + kind); + + DateTime dateTime_Token_ThisDay_ThisMonth = DateTime.MinValue; + bool parsedDayThisMonth = false; + while (parsedDayThisMonth == false) + { + try + { + dateTime_Token_ThisDay_ThisMonth = new DateTime( + dateTimeNowOriginal.Year, + dateTimeNowOriginal.Month, + dayOfMonth, + 0, 0, 0, + kind); + + parsedDayThisMonth = true; + } + catch (ArgumentOutOfRangeException ex) + { + // Date out of range + dayOfMonth--; + } + } + if (dateTime_Token_ThisDay_ThisMonth > dateTime_Token_Today) + { + // This day is in the future. Must go back + markDate = dateTime_Token_ThisDay_ThisMonth.AddMonths(-1); + } + else + { + // This day of month is today or in the past. Take it + markDate = dateTime_Token_ThisDay_ThisMonth; + } + } + } + } + else + { + logger.Error("Job File Problem: Input.TimeFrame.MarkDate={0} is not a valid token", jobConfiguration.Input.TimeFrame.MarkDate); + } + + break; + } } - if (jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience == null) - { - jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience = new JobUserExperience(); - jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience.Normal = true; - jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience.Slow = true; - jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience.VerySlow = true; - jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience.Stall = true; - jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience.Error = true; + if (markDate == DateTime.MinValue) + { + loggerConsole.Error("Job File Problem: Input.TimeFrame.MarkDate={0} is not a valid Date or recognized token", jobConfiguration.Input.TimeFrame.MarkDate); + return; } - if (jobConfiguration.Input.SnapshotSelectionCriteria.SnapshotType == null) + // Parse out time of the point from which we're counting + DateTime markTime = DateTime.MinValue; + if (DateTime.TryParse(jobConfiguration.Input.TimeFrame.MarkTime, null, DateTimeStyles.AssumeLocal, out markTime) == true) { - jobConfiguration.Input.SnapshotSelectionCriteria.SnapshotType = new JobSnapshotType(); - jobConfiguration.Input.SnapshotSelectionCriteria.SnapshotType.Full = true; - jobConfiguration.Input.SnapshotSelectionCriteria.SnapshotType.Partial = true; - jobConfiguration.Input.SnapshotSelectionCriteria.SnapshotType.None = true; + //markTime = markTime.ToUniversalTime(); } - - // Validate Entity Dashboard Screenshot selection criteria - if (jobConfiguration.Input.EntityDashboardSelectionCriteria == null) + else { - jobConfiguration.Input.EntityDashboardSelectionCriteria = new JobEntityDashboardSelectionCriteria(); + logger.Warn("Job File Problem: Input.TimeFrame.MarkTime={0} is not a valid Time", jobConfiguration.Input.TimeFrame.MarkTime); + + // Let's try one of the tokens + switch (jobConfiguration.Input.TimeFrame.MarkTime.ToUpper()) + { + case "NOW": + // Round the time out to the beginning of the minute + markTime = new DateTime( + dateTimeNowOriginal.Year, + dateTimeNowOriginal.Month, + dateTimeNowOriginal.Day, + dateTimeNowOriginal.Hour, + dateTimeNowOriginal.Minute, + 0, + dateTimeNowOriginal.Kind); + + break; + + case "NOW_Z": + // Round the time out to the beginning of the minute + markTime = new DateTime( + dateTimeNowOriginal.Year, + dateTimeNowOriginal.Month, + dateTimeNowOriginal.Day, + dateTimeNowOriginal.ToUniversalTime().Hour, + dateTimeNowOriginal.ToUniversalTime().Minute, + 0, + DateTimeKind.Utc); + + break; + + case "CURRENT_HOUR": + // Round the time out to the beginning of hour + markTime = new DateTime( + dateTimeNowOriginal.Year, + dateTimeNowOriginal.Month, + dateTimeNowOriginal.Day, + dateTimeNowOriginal.Hour, + 0, + 0, + dateTimeNowOriginal.Kind); + + break; + + case "CURRENT_HOUR_Z": + // Round the time out to the beginning of hour + markTime = new DateTime( + dateTimeNowOriginal.Year, + dateTimeNowOriginal.Month, + dateTimeNowOriginal.Day, + dateTimeNowOriginal.ToUniversalTime().Minute, + 0, + 0, + DateTimeKind.Utc); + + break; + + case "PREVIOUS_HOUR": + // Round the time out to the beginning of past + markTime = new DateTime( + dateTimeNowOriginal.Year, + dateTimeNowOriginal.Month, + dateTimeNowOriginal.Day, + dateTimeNowOriginal.Hour, + 0, + 0, + dateTimeNowOriginal.Kind).AddHours(-1); + + break; + + case "PREVIOUS_HOUR_Z": + // Round the time out to the beginning of past + markTime = new DateTime( + dateTimeNowOriginal.Year, + dateTimeNowOriginal.Month, + dateTimeNowOriginal.Day, + dateTimeNowOriginal.ToUniversalTime().Hour, + 0, + 0, + DateTimeKind.Utc).AddHours(-1); + + break; + + default: + logger.Warn("Job File Problem: Input.TimeFrame.MarkTime={0} is not a valid token", jobConfiguration.Input.TimeFrame.MarkTime); + + break; + } } - if (jobConfiguration.Input.EntityDashboardSelectionCriteria.Tiers == null) + if (markTime == DateTime.MinValue) { - jobConfiguration.Input.EntityDashboardSelectionCriteria.Tiers = new string[0]; + loggerConsole.Error("Job File Problem: Input.TimeFrame.MarkTime={0} is not a valid Time or recognized token", jobConfiguration.Input.TimeFrame.MarkTime); + return; } - if (jobConfiguration.Input.EntityDashboardSelectionCriteria.BusinessTransactions == null) + // Parse out duration, first the .NET + TimeSpan duration = new TimeSpan(0, 0, 0); + if (TimeSpan.TryParse(jobConfiguration.Input.TimeFrame.Duration, out duration) == false) { - jobConfiguration.Input.EntityDashboardSelectionCriteria.BusinessTransactions = new string[0]; - } + // Not a Timespan + logger.Warn("Job File Problem: Input.TimeFrame.Duration={0} is not a valid TimeSpan", jobConfiguration.Input.TimeFrame.Duration); + loggerConsole.Warn("Job File Problem: Input.TimeFrame.Duration={0} is not a valid TimeSpan", jobConfiguration.Input.TimeFrame.Duration); + + // Try the ISO 8601 time interval + try + { + duration = XmlConvert.ToTimeSpan(jobConfiguration.Input.TimeFrame.Duration); + } + catch (FormatException ex) + { + logger.Error("Job File Problem: Input.TimeFrame.Duration={0} is not a valid ISO 8601 Time interval", jobConfiguration.Input.TimeFrame.Duration); + loggerConsole.Error("Job File Problem: Input.TimeFrame.Duration={0} is not a valid ISO 8601 Time interval", jobConfiguration.Input.TimeFrame.Duration); - if (jobConfiguration.Input.EntityDashboardSelectionCriteria.TierType == null) + return; + } + } + if (Math.Abs(duration.TotalSeconds) < 60) { - jobConfiguration.Input.EntityDashboardSelectionCriteria.TierType = new JobTierType(); - jobConfiguration.Input.EntityDashboardSelectionCriteria.TierType.All = true; + logger.Error("Job File Problem: Input.TimeFrame.Duration={0} is shorter than 60 seconds", jobConfiguration.Input.TimeFrame.Duration); + loggerConsole.Error("Job File Problem: Input.TimeFrame.Duration={0} is is shorter than 60 seconds", jobConfiguration.Input.TimeFrame.Duration); + + return; } - if (jobConfiguration.Input.EntityDashboardSelectionCriteria.NodeType == null) + // If we got here, we successfully parsed the date, time and duration (possibly negative) + logger.Info("Parsed MarkDate '{0}' as '{1:o}', local '{2:o}'", jobConfiguration.Input.TimeFrame.MarkDate, markDate.ToUniversalTime(), markDate.ToLocalTime()); + logger.Info("Parsed MarkTime '{0}' as '{1:o}', local '{2:o}'", jobConfiguration.Input.TimeFrame.MarkTime, markTime.ToUniversalTime(), markTime.ToLocalTime()); + logger.Info("Parsed Duration '{0}' as '{1:c}'", jobConfiguration.Input.TimeFrame.Duration, duration); + loggerConsole.Info("Parsed MarkDate '{0}' as '{1:o}', local '{2:o}'", jobConfiguration.Input.TimeFrame.MarkDate, markDate.ToUniversalTime(), markDate.ToLocalTime()); + loggerConsole.Info("Parsed MarkTime '{0}' as '{1:o}', local '{2:o}'", jobConfiguration.Input.TimeFrame.MarkTime, markTime.ToUniversalTime(), markTime.ToLocalTime()); + loggerConsole.Info("Parsed Duration '{0}' as '{1:c}'", jobConfiguration.Input.TimeFrame.Duration, duration); + + DateTime dateTimeMark = DateTime.MinValue; + if ((markDate.Kind == DateTimeKind.Local && markTime.Kind == DateTimeKind.Local) || (markDate.Kind == DateTimeKind.Utc && markTime.Kind == DateTimeKind.Utc)) { - jobConfiguration.Input.EntityDashboardSelectionCriteria.NodeType = new JobTierType(); - jobConfiguration.Input.EntityDashboardSelectionCriteria.NodeType.All = true; + dateTimeMark = new DateTime( + markDate.Year, + markDate.Month, + markDate.Day, + markTime.Hour, + markTime.Minute, + markTime.Second, + markDate.Kind); } - - if (jobConfiguration.Input.EntityDashboardSelectionCriteria.BusinessTransactionType == null) + else if (markDate.Kind == DateTimeKind.Local && markTime.Kind == DateTimeKind.Utc) { - jobConfiguration.Input.EntityDashboardSelectionCriteria.BusinessTransactionType = new JobBusinessTransactionType(); - jobConfiguration.Input.EntityDashboardSelectionCriteria.BusinessTransactionType.All = true; + dateTimeMark = new DateTime( + markDate.Year, + markDate.Month, + markDate.Day, + markTime.ToLocalTime().Hour, + markTime.ToLocalTime().Minute, + markTime.ToLocalTime().Second, + markDate.Kind); } - - if (jobConfiguration.Input.EntityDashboardSelectionCriteria.BackendType == null) + else if (markDate.Kind == DateTimeKind.Utc && markTime.Kind == DateTimeKind.Local) { - jobConfiguration.Input.EntityDashboardSelectionCriteria.BackendType = new JobBackendType(); - jobConfiguration.Input.EntityDashboardSelectionCriteria.BackendType.All = true; + dateTimeMark = new DateTime( + markDate.Year, + markDate.Month, + markDate.Day, + markTime.ToUniversalTime().Hour, + markTime.ToUniversalTime().Minute, + markTime.ToUniversalTime().Second, + markDate.Kind); } - // Validate Configuration Comparison selection - if (jobConfiguration.Input.ConfigurationComparisonReferenceAPM == null || - jobConfiguration.Input.ConfigurationComparisonReferenceAPM.Controller == null || jobConfiguration.Input.ConfigurationComparisonReferenceAPM.Controller.Length == 0 || - jobConfiguration.Input.ConfigurationComparisonReferenceAPM.Application == null || jobConfiguration.Input.ConfigurationComparisonReferenceAPM.Application.Length == 0) + DateTime dateTimeMarkPlusDuration = dateTimeMark.Add(duration); + + logger.Info("DateTimeMark '{0:o}', local '{1:o}'", dateTimeMark.ToUniversalTime(), dateTimeMark.ToLocalTime()); + logger.Info("DateTimeMarkPlusDuration '{0:o}', local '{1:o}'", dateTimeMarkPlusDuration.ToUniversalTime(), dateTimeMarkPlusDuration.ToLocalTime()); + loggerConsole.Info("DateTimeMark '{0:o}', local '{1:o}'", dateTimeMark.ToUniversalTime(), dateTimeMark.ToLocalTime()); + loggerConsole.Info("DateTimeMarkPlusDuration '{0:o}', local '{1:o}'", dateTimeMarkPlusDuration.ToUniversalTime(), dateTimeMarkPlusDuration.ToLocalTime()); + + jobConfiguration.Input.TimeRange = new JobTimeRange(); + if (dateTimeMark <= dateTimeMarkPlusDuration) { - jobConfiguration.Input.ConfigurationComparisonReferenceAPM = new JobTarget(); - jobConfiguration.Input.ConfigurationComparisonReferenceAPM.Controller = JobStepBase.BLANK_APPLICATION_CONTROLLER; - jobConfiguration.Input.ConfigurationComparisonReferenceAPM.Application = JobStepBase.BLANK_APPLICATION_APM; - jobConfiguration.Input.ConfigurationComparisonReferenceAPM.Type = JobStepBase.APPLICATION_TYPE_APM; + jobConfiguration.Input.TimeRange.From = dateTimeMark; + jobConfiguration.Input.TimeRange.To = dateTimeMarkPlusDuration; } else { - jobConfiguration.Input.ConfigurationComparisonReferenceAPM.Controller = jobConfiguration.Input.ConfigurationComparisonReferenceAPM.Controller; - jobConfiguration.Input.ConfigurationComparisonReferenceAPM.Type = JobStepBase.APPLICATION_TYPE_APM; + jobConfiguration.Input.TimeRange.From = dateTimeMarkPlusDuration; + jobConfiguration.Input.TimeRange.To = dateTimeMark; } - if (jobConfiguration.Input.ConfigurationComparisonReferenceWEB == null || - jobConfiguration.Input.ConfigurationComparisonReferenceWEB.Controller == null || jobConfiguration.Input.ConfigurationComparisonReferenceWEB.Controller.Length == 0 || - jobConfiguration.Input.ConfigurationComparisonReferenceWEB.Application == null || jobConfiguration.Input.ConfigurationComparisonReferenceWEB.Application.Length == 0) + #endregion + } + else if (jobConfiguration.Input.TimeRange != null) + { + #region Validate old style TimeRange + + //"TimeRange": { + // "From": "2020-04-22T23:00:00", + // "To": "2020-04-23T00:00:00" + //} + // Or + //"TimeRange": { + // "From": "2020-04-22T23:00:00Z", + // "To": "2020-04-23T00:00:00Z" + //} + + // The older type of explicit time range specifier in older job files + + if (jobConfiguration.Input.TimeRange.From == null || jobConfiguration.Input.TimeRange.From == DateTime.MinValue) { - jobConfiguration.Input.ConfigurationComparisonReferenceWEB = new JobTarget(); - jobConfiguration.Input.ConfigurationComparisonReferenceWEB.Controller = JobStepBase.BLANK_APPLICATION_CONTROLLER; - jobConfiguration.Input.ConfigurationComparisonReferenceWEB.Application = JobStepBase.BLANK_APPLICATION_WEB; - jobConfiguration.Input.ConfigurationComparisonReferenceWEB.Type = JobStepBase.APPLICATION_TYPE_WEB; + logger.Error("Job File Problem: Input.TimeRange.From can not be empty"); + loggerConsole.Error("Job File Problem: Input.TimeRange.From can not be empty"); + + return; } - else + else if (jobConfiguration.Input.TimeRange.To == null || jobConfiguration.Input.TimeRange.To == DateTime.MinValue) { - jobConfiguration.Input.ConfigurationComparisonReferenceWEB.Controller = jobConfiguration.Input.ConfigurationComparisonReferenceWEB.Controller; - jobConfiguration.Input.ConfigurationComparisonReferenceWEB.Type = JobStepBase.APPLICATION_TYPE_WEB; + logger.Error("Job File Problem: Input.TimeRange.To can not be empty"); + loggerConsole.Error("Job File Problem: Input.TimeRange.To can not be empty"); + + return; } - if (jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE == null || - jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE.Controller == null || jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE.Controller.Length == 0 || - jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE.Application == null || jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE.Application.Length == 0) + #endregion + } + + else + { + // Either TimeFrame or TimeRange must be specified. TimeRange is older style for explicit saying, TimeFrame is for the newer one + logger.Error("Job File Problem: Input.TimeRange and Input.TimeFrame can not both be empty"); + loggerConsole.Error("Job File Problem: Input.TimeRange and Input.TimeFrame can not both be empty"); + + return; + } + + // Switch all times in TimeRange to UTC + jobConfiguration.Input.TimeRange.From = jobConfiguration.Input.TimeRange.From.ToUniversalTime(); + jobConfiguration.Input.TimeRange.To = jobConfiguration.Input.TimeRange.To.ToUniversalTime(); + + // Now measure the durations, make sure that From is before To + if (jobConfiguration.Input.TimeRange.From > jobConfiguration.Input.TimeRange.To) + { + logger.Error("Job File Problem: Input.TimeRange.From='{0:o}' can not be > Input.TimeRange.To='{1:o}'", jobConfiguration.Input.TimeRange.From, jobConfiguration.Input.TimeRange.To); + loggerConsole.Error("Job File Problem: Input.TimeRange.From='{0:o}' can not be > Input.TimeRange.To='{1:o}'", jobConfiguration.Input.TimeRange.From, jobConfiguration.Input.TimeRange.To); + + return; + } + + // Make sure we are not past the Now for the From + if (jobConfiguration.Input.TimeRange.From > dateTimeNowOriginalUtc) + { + logger.Error("Job File Problem: Input.TimeRange.From='{0:o}' can not be in the future", jobConfiguration.Input.TimeRange.From); + loggerConsole.Error("Job File Problem: Input.TimeRange.From='{0:o}' can not be in the future", jobConfiguration.Input.TimeRange.From); + + return; + } + + // Make sure we are not past the Now for the To, if yes, set it to Now + if (jobConfiguration.Input.TimeRange.To > dateTimeNowOriginalUtc) + { + logger.Warn("Job File Problem: Input.TimeRange.To='{0:o}' can not be in the future", jobConfiguration.Input.TimeRange.To); + loggerConsole.Warn("Job File Problem: Input.TimeRange.To='{0:o}' can not be in the future, setting to Now", jobConfiguration.Input.TimeRange.To); + + jobConfiguration.Input.TimeRange.To = dateTimeNowOriginalUtc; + } + + logger.Info("UTC Input.TimeRange.From='{0:o}' to Input.TimeRange.To='{1:o}'", jobConfiguration.Input.TimeRange.From, jobConfiguration.Input.TimeRange.To); + loggerConsole.Info("UTC Input.TimeRange.From='{0:o}' to Input.TimeRange.To='{1:o}'", jobConfiguration.Input.TimeRange.From, jobConfiguration.Input.TimeRange.To); + logger.Info("Local Input.TimeRange.From='{0:o}' to Input.TimeRange.To='{1:o}'", jobConfiguration.Input.TimeRange.From.ToLocalTime(), jobConfiguration.Input.TimeRange.To.ToLocalTime()); + loggerConsole.Info("Local Input.TimeRange.From='{0:o}' to Input.TimeRange.To='{1:o}'", jobConfiguration.Input.TimeRange.From.ToLocalTime(), jobConfiguration.Input.TimeRange.To.ToLocalTime()); + + #endregion + + #region Validate Input Everything else + + // Validate Metrics selection criteria + if (jobConfiguration.Input.MetricsSelectionCriteria == null) + { + jobConfiguration.Input.MetricsSelectionCriteria = new string[0]; + } + + // Validate Events selection criteria + if (jobConfiguration.Input.EventsSelectionCriteria == null) + { + jobConfiguration.Input.EventsSelectionCriteria = new string[0]; + } + + // Validate Snapshot selection criteria + if (jobConfiguration.Input.SnapshotSelectionCriteria == null) + { + jobConfiguration.Input.SnapshotSelectionCriteria = new JobSnapshotSelectionCriteria(); + } + + if (jobConfiguration.Input.SnapshotSelectionCriteria.Tiers == null) + { + jobConfiguration.Input.SnapshotSelectionCriteria.Tiers = new string[0]; + } + + if (jobConfiguration.Input.SnapshotSelectionCriteria.BusinessTransactions == null) + { + jobConfiguration.Input.SnapshotSelectionCriteria.BusinessTransactions = new string[0]; + } + + if (jobConfiguration.Input.SnapshotSelectionCriteria.TierTypes == null) + { + jobConfiguration.Input.SnapshotSelectionCriteria.TierTypes = new string[1]; + jobConfiguration.Input.SnapshotSelectionCriteria.TierTypes[0] = "All"; + } + + if (jobConfiguration.Input.SnapshotSelectionCriteria.BusinessTransactionTypes == null) + { + jobConfiguration.Input.SnapshotSelectionCriteria.BusinessTransactionTypes = new string[1]; + jobConfiguration.Input.SnapshotSelectionCriteria.BusinessTransactionTypes[0] = "All"; + } + + if (jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience == null) + { + jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience = new JobUserExperience(); + jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience.Normal = true; + jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience.Slow = true; + jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience.VerySlow = true; + jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience.Stall = true; + jobConfiguration.Input.SnapshotSelectionCriteria.UserExperience.Error = true; + } + + if (jobConfiguration.Input.SnapshotSelectionCriteria.SnapshotType == null) + { + jobConfiguration.Input.SnapshotSelectionCriteria.SnapshotType = new JobSnapshotType(); + jobConfiguration.Input.SnapshotSelectionCriteria.SnapshotType.Full = true; + jobConfiguration.Input.SnapshotSelectionCriteria.SnapshotType.Partial = true; + jobConfiguration.Input.SnapshotSelectionCriteria.SnapshotType.None = true; + } + + // Validate Entity Dashboard Screenshot selection criteria + if (jobConfiguration.Input.EntityDashboardSelectionCriteria == null) + { + jobConfiguration.Input.EntityDashboardSelectionCriteria = new JobEntityDashboardSelectionCriteria(); + } + + if (jobConfiguration.Input.EntityDashboardSelectionCriteria.Tiers == null) + { + jobConfiguration.Input.EntityDashboardSelectionCriteria.Tiers = new string[0]; + } + + if (jobConfiguration.Input.EntityDashboardSelectionCriteria.BusinessTransactions == null) + { + jobConfiguration.Input.EntityDashboardSelectionCriteria.BusinessTransactions = new string[0]; + } + + if (jobConfiguration.Input.EntityDashboardSelectionCriteria.TierTypes == null) + { + jobConfiguration.Input.EntityDashboardSelectionCriteria.TierTypes = new string[1]; + jobConfiguration.Input.EntityDashboardSelectionCriteria.TierTypes[0] = "All"; + } + + if (jobConfiguration.Input.EntityDashboardSelectionCriteria.NodeTypes == null) + { + jobConfiguration.Input.EntityDashboardSelectionCriteria.NodeTypes = new string[1]; + jobConfiguration.Input.EntityDashboardSelectionCriteria.NodeTypes[0] = "All"; + } + + if (jobConfiguration.Input.EntityDashboardSelectionCriteria.BusinessTransactionTypes == null) + { + jobConfiguration.Input.EntityDashboardSelectionCriteria.BusinessTransactionTypes = new string[1]; + jobConfiguration.Input.EntityDashboardSelectionCriteria.BusinessTransactionTypes[0] = "All"; + } + + if (jobConfiguration.Input.EntityDashboardSelectionCriteria.BackendTypes == null) + { + jobConfiguration.Input.EntityDashboardSelectionCriteria.BackendTypes = new string[1]; + jobConfiguration.Input.EntityDashboardSelectionCriteria.BackendTypes[0] = "All"; + } + + // Validate Configuration Comparison selection + if (jobConfiguration.Input.ConfigurationComparisonReferenceAPM == null || + jobConfiguration.Input.ConfigurationComparisonReferenceAPM.Controller == null || jobConfiguration.Input.ConfigurationComparisonReferenceAPM.Controller.Length == 0 || + jobConfiguration.Input.ConfigurationComparisonReferenceAPM.Application == null || jobConfiguration.Input.ConfigurationComparisonReferenceAPM.Application.Length == 0) + { + jobConfiguration.Input.ConfigurationComparisonReferenceAPM = new JobTarget(); + jobConfiguration.Input.ConfigurationComparisonReferenceAPM.Controller = JobStepBase.BLANK_APPLICATION_CONTROLLER; + jobConfiguration.Input.ConfigurationComparisonReferenceAPM.Application = JobStepBase.BLANK_APPLICATION_APM; + jobConfiguration.Input.ConfigurationComparisonReferenceAPM.Type = JobStepBase.APPLICATION_TYPE_APM; + } + else + { + jobConfiguration.Input.ConfigurationComparisonReferenceAPM.Controller = jobConfiguration.Input.ConfigurationComparisonReferenceAPM.Controller; + jobConfiguration.Input.ConfigurationComparisonReferenceAPM.Type = JobStepBase.APPLICATION_TYPE_APM; + } + + if (jobConfiguration.Input.ConfigurationComparisonReferenceWEB == null || + jobConfiguration.Input.ConfigurationComparisonReferenceWEB.Controller == null || jobConfiguration.Input.ConfigurationComparisonReferenceWEB.Controller.Length == 0 || + jobConfiguration.Input.ConfigurationComparisonReferenceWEB.Application == null || jobConfiguration.Input.ConfigurationComparisonReferenceWEB.Application.Length == 0) + { + jobConfiguration.Input.ConfigurationComparisonReferenceWEB = new JobTarget(); + jobConfiguration.Input.ConfigurationComparisonReferenceWEB.Controller = JobStepBase.BLANK_APPLICATION_CONTROLLER; + jobConfiguration.Input.ConfigurationComparisonReferenceWEB.Application = JobStepBase.BLANK_APPLICATION_WEB; + jobConfiguration.Input.ConfigurationComparisonReferenceWEB.Type = JobStepBase.APPLICATION_TYPE_WEB; + } + else + { + jobConfiguration.Input.ConfigurationComparisonReferenceWEB.Controller = jobConfiguration.Input.ConfigurationComparisonReferenceWEB.Controller; + jobConfiguration.Input.ConfigurationComparisonReferenceWEB.Type = JobStepBase.APPLICATION_TYPE_WEB; + } + + if (jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE == null || + jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE.Controller == null || jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE.Controller.Length == 0 || + jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE.Application == null || jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE.Application.Length == 0) + { + jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE = new JobTarget(); + jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE.Controller = JobStepBase.BLANK_APPLICATION_CONTROLLER; + jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE.Application = JobStepBase.BLANK_APPLICATION_MOBILE; + jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE.Type = JobStepBase.APPLICATION_TYPE_MOBILE; + } + else + { + jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE.Controller = jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE.Controller; + jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE.Type = JobStepBase.APPLICATION_TYPE_MOBILE; + } + + if (jobConfiguration.Input.ConfigurationComparisonReferenceDB == null || + jobConfiguration.Input.ConfigurationComparisonReferenceDB.Controller == null || jobConfiguration.Input.ConfigurationComparisonReferenceDB.Controller.Length == 0 || + jobConfiguration.Input.ConfigurationComparisonReferenceDB.Application == null || jobConfiguration.Input.ConfigurationComparisonReferenceDB.Application.Length == 0) + { + jobConfiguration.Input.ConfigurationComparisonReferenceDB = new JobTarget(); + jobConfiguration.Input.ConfigurationComparisonReferenceDB.Controller = JobStepBase.BLANK_APPLICATION_CONTROLLER; + jobConfiguration.Input.ConfigurationComparisonReferenceDB.Application = JobStepBase.BLANK_APPLICATION_DB; + jobConfiguration.Input.ConfigurationComparisonReferenceDB.Type = JobStepBase.APPLICATION_TYPE_DB; + } + else + { + jobConfiguration.Input.ConfigurationComparisonReferenceDB.Controller = jobConfiguration.Input.ConfigurationComparisonReferenceDB.Controller; + jobConfiguration.Input.ConfigurationComparisonReferenceDB.Type = JobStepBase.APPLICATION_TYPE_DB; + } + + #endregion + + #region Expand time ranges into hourly chunks + + // Prepare list of time ranges that goes from the Hour:00 of the From to the Hour:59 of the To + jobConfiguration.Input.HourlyTimeRanges = new List(); + + DateTime intervalStartTime = jobConfiguration.Input.TimeRange.From; + DateTime intervalEndTime = new DateTime( + intervalStartTime.Year, + intervalStartTime.Month, + intervalStartTime.Day, + intervalStartTime.Hour, + 0, + 0, + DateTimeKind.Utc).AddHours(1); + do + { + TimeSpan timeSpan = intervalEndTime - jobConfiguration.Input.TimeRange.To; + if (timeSpan.TotalMinutes >= 0) { - jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE = new JobTarget(); - jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE.Controller = JobStepBase.BLANK_APPLICATION_CONTROLLER; - jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE.Application = JobStepBase.BLANK_APPLICATION_MOBILE; - jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE.Type = JobStepBase.APPLICATION_TYPE_MOBILE; + jobConfiguration.Input.HourlyTimeRanges.Add(new JobTimeRange { From = intervalStartTime, To = jobConfiguration.Input.TimeRange.To }); + break; } else { - jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE.Controller = jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE.Controller; - jobConfiguration.Input.ConfigurationComparisonReferenceMOBILE.Type = JobStepBase.APPLICATION_TYPE_MOBILE; + jobConfiguration.Input.HourlyTimeRanges.Add(new JobTimeRange { From = intervalStartTime, To = intervalEndTime }); } - if (jobConfiguration.Input.ConfigurationComparisonReferenceDB == null || - jobConfiguration.Input.ConfigurationComparisonReferenceDB.Controller == null || jobConfiguration.Input.ConfigurationComparisonReferenceDB.Controller.Length == 0 || - jobConfiguration.Input.ConfigurationComparisonReferenceDB.Application == null || jobConfiguration.Input.ConfigurationComparisonReferenceDB.Application.Length == 0) + intervalStartTime = intervalEndTime; + intervalEndTime = intervalStartTime.AddHours(1); + } + while (true); + + #endregion + + #region Get job file name + + // Job name is derived from the file name, start date and number of hourly chunks in that range + programOptions.JobName = String.Format("{0}.{1:yyyyMMddHHmm}.{2}", + Path.GetFileNameWithoutExtension(programOptions.InputETLJobFilePath), + jobConfiguration.Input.TimeRange.From, + jobConfiguration.Input.HourlyTimeRanges.Count); + + #endregion + + #region Create output folder + + // If output folder isn't specified, get default output folder + if (programOptions.OutputFolderPath == null || programOptions.OutputFolderPath.Length == 0) + { + // Windows: at the root of C: on Windows + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) == true) { - jobConfiguration.Input.ConfigurationComparisonReferenceDB = new JobTarget(); - jobConfiguration.Input.ConfigurationComparisonReferenceDB.Controller = JobStepBase.BLANK_APPLICATION_CONTROLLER; - jobConfiguration.Input.ConfigurationComparisonReferenceDB.Application = JobStepBase.BLANK_APPLICATION_DB; - jobConfiguration.Input.ConfigurationComparisonReferenceDB.Type = JobStepBase.APPLICATION_TYPE_DB; + programOptions.OutputFolderPath = @"C:\AppD.Dexter.Out"; } - else + // Mac/Linux: a child of %HOME% path + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) == true || RuntimeInformation.IsOSPlatform(OSPlatform.OSX) == true) { - jobConfiguration.Input.ConfigurationComparisonReferenceDB.Controller = jobConfiguration.Input.ConfigurationComparisonReferenceDB.Controller; - jobConfiguration.Input.ConfigurationComparisonReferenceDB.Type = JobStepBase.APPLICATION_TYPE_DB; + programOptions.OutputFolderPath = Path.Combine(Environment.GetEnvironmentVariable("HOME"), "AppD.Dexter.Out"); } + } - #endregion + programOptions.OutputFolderPath = Path.GetFullPath(programOptions.OutputFolderPath); - #region Validate Output + logger.Info("Creating output folder {0}", programOptions.OutputFolderPath); + loggerConsole.Info("Creating output folder {0}", programOptions.OutputFolderPath); - if (jobConfiguration.Output == null) - { - logger.Error("Job File Problem: Output can not be empty"); - loggerConsole.Error("Job File Problem: Output can not be empty"); + if (FileIOHelper.CreateFolder(programOptions.OutputFolderPath) == false) + { + logger.Error("Unable to create output folder={0}", programOptions.OutputFolderPath); + loggerConsole.Error("Unable to create output folder={0}", programOptions.OutputFolderPath); - return; - } + return; + } - #endregion + #endregion - #region Expand time ranges into hourly chunks - - // Prepare list of time ranges that goes from the Hour:00 of the From to the Hour:59 of the To - jobConfiguration.Input.HourlyTimeRanges = new List(); - - DateTime intervalStartTime = jobConfiguration.Input.TimeRange.From; - DateTime intervalEndTime = new DateTime( - intervalStartTime.Year, - intervalStartTime.Month, - intervalStartTime.Day, - intervalStartTime.Hour, - 0, - 0, - DateTimeKind.Utc).AddHours(1); - do + #region Create job output folder + + // Set up the output job folder and job file path + //programOptions.JobName = Path.GetFileNameWithoutExtension(programOptions.InputETLJobFilePath); + programOptions.OutputJobFolderPath = Path.Combine(programOptions.OutputFolderPath, programOptions.JobName); + programOptions.OutputJobFilePath = Path.Combine(programOptions.OutputJobFolderPath, "jobparameters.json"); + programOptions.ProgramLocationFolderPath = AppDomain.CurrentDomain.BaseDirectory; + + // Create job folder if it doesn't exist + // or + // Clear out job folder if it already exists and restart of the job was requested + if (programOptions.DeletePreviousJobOutput) + { + if (FileIOHelper.DeleteFolder(programOptions.OutputJobFolderPath) == false) { - TimeSpan timeSpan = intervalEndTime - jobConfiguration.Input.TimeRange.To; - if (timeSpan.TotalMinutes >= 0) - { - jobConfiguration.Input.HourlyTimeRanges.Add(new JobTimeRange { From = intervalStartTime, To = jobConfiguration.Input.TimeRange.To }); - break; - } - else - { - jobConfiguration.Input.HourlyTimeRanges.Add(new JobTimeRange { From = intervalStartTime, To = intervalEndTime }); - } + logger.Error("Unable to clear job folder {0}", programOptions.OutputJobFolderPath); + loggerConsole.Error("Unable to clear job folder {0}", programOptions.OutputJobFolderPath); - intervalStartTime = intervalEndTime; - intervalEndTime = intervalStartTime.AddHours(1); + return; } - while (true); - #endregion + // Sleep after deleting to let the file system catch up + Thread.Sleep(2000); + } - #region Validate list of targets + logger.Info("Creating job output folder {0}", programOptions.OutputJobFolderPath); + loggerConsole.Info("Creating job output folder {0}", programOptions.OutputJobFolderPath); - // Validate list of targets - if (jobConfiguration.Target == null || jobConfiguration.Target.Count == 0) - { - logger.Error("Job File Problem: No targets to work on"); - loggerConsole.Error("Job File Problem: No targets to work on"); + if (FileIOHelper.CreateFolder(programOptions.OutputJobFolderPath) == false) + { + logger.Error("Unable to create job output folder={0}", programOptions.OutputJobFolderPath); + loggerConsole.Error("Unable to create job output folder={0}", programOptions.OutputJobFolderPath); - return; - } + return; + } - #endregion + #endregion + + #region Process input job file to output job file + + // Check if this job file was already already validated and exists in target folder + loggerConsole.Info("Processing input job file to output job file"); + if (Directory.Exists(programOptions.OutputJobFolderPath) == false || File.Exists(programOptions.OutputJobFilePath) == false) + { + // New job + // Expand list of targets from the input file + // Save validated job file to the output directory #region Expand list of targets @@ -1030,6 +1590,19 @@ public static void RunProgramETL(ProgramOptions programOptions) return; } + + // Also save a copy of the original file name + + string copyOfJobFileInOriginalName = Path.Combine( + programOptions.OutputJobFolderPath, + Path.GetFileName(programOptions.InputETLJobFilePath)); + + if (FileIOHelper.WriteJobConfigurationToFile(jobConfiguration, copyOfJobFileInOriginalName) == false) + { + loggerConsole.Error("Unable to write job input file {0}", programOptions.OutputJobFilePath); + + return; + } } else { @@ -1048,7 +1621,7 @@ public static void RunProgramETL(ProgramOptions programOptions) JObject licenseFile = FileIOHelper.LoadJObjectFromFile(programLicensePath); JObject licensedFeatures = (JObject)licenseFile["LicensedFeatures"]; - string dataSigned = licensedFeatures.ToString(Formatting.None); + string dataSigned = licensedFeatures.ToString(Newtonsoft.Json.Formatting.None); var bytesSigned = Encoding.UTF8.GetBytes(dataSigned); string dataSignature = licenseFile["Signature"].ToString(); @@ -1064,7 +1637,13 @@ public static void RunProgramETL(ProgramOptions programOptions) bool licenseValidationResult = rsaPublicKey.VerifyData(bytesSigned, bytesSignature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); - logger.Info("Validating license\n{0}\nwith signature {1}\nfrom {2} containing \n{3} returned {4}", dataSigned, dataSignature, licenseCertificatePath, publicCert, licenseValidationResult); + logger.Info( +@"Validating license +{0} +with signature {1} +from {2} containing +{3} returned {4}", + dataSigned, dataSignature, licenseCertificatePath, publicCert, licenseValidationResult); JobOutput licensedReports = new JobOutput(); licensedReports.ApplicationSummary = true; @@ -1135,10 +1714,99 @@ public static void RunProgramETL(ProgramOptions programOptions) public static void RunProgramCompare(ProgramOptions programOptions) { + #region Validate job file exists + + programOptions.InputCompareJobFilePath = Path.GetFullPath(programOptions.InputCompareJobFilePath); + + logger.Info("Checking input Compare job file {0}", programOptions.InputCompareJobFilePath); + loggerConsole.Info("Checking input Compare job file {0}", programOptions.InputCompareJobFilePath); + + if (File.Exists(programOptions.InputCompareJobFilePath) == false) + { + logger.Error("Compare job file {0} does not exist", programOptions.InputCompareJobFilePath); + loggerConsole.Error("Compare job file {0} does not exist", programOptions.InputCompareJobFilePath); + + return; + } + + #endregion + + #region Create output folder + + // If output folder isn't specified, get default output folder + if (programOptions.OutputFolderPath == null || programOptions.OutputFolderPath.Length == 0) + { + // Windows: at the root of C: on Windows + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) == true) + { + programOptions.OutputFolderPath = @"C:\AppD.Dexter.Out"; + } + // Mac/Linux: a child of %HOME% path + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) == true || RuntimeInformation.IsOSPlatform(OSPlatform.OSX) == true) + { + programOptions.OutputFolderPath = Path.Combine(Environment.GetEnvironmentVariable("HOME"), "AppD.Dexter.Out"); + } + } + + programOptions.OutputFolderPath = Path.GetFullPath(programOptions.OutputFolderPath); + + logger.Info("Creating output folder {0}", programOptions.OutputFolderPath); + loggerConsole.Info("Creating output folder {0}", programOptions.OutputFolderPath); + + if (FileIOHelper.CreateFolder(programOptions.OutputFolderPath) == false) + { + logger.Error("Unable to create output folder={0}", programOptions.OutputFolderPath); + loggerConsole.Error("Unable to create output folder={0}", programOptions.OutputFolderPath); + + return; + } + + #endregion + + #region Create compare output folder + + // Set up the output job folder and job file path + programOptions.JobName = Path.GetFileNameWithoutExtension(programOptions.InputCompareJobFilePath); + programOptions.OutputJobFolderPath = Path.Combine(programOptions.OutputFolderPath, programOptions.JobName); + programOptions.OutputJobFilePath = Path.Combine(programOptions.OutputJobFolderPath, "compareparameters.json"); + programOptions.ProgramLocationFolderPath = AppDomain.CurrentDomain.BaseDirectory; + + // Create job folder if it doesn't exist + // or + // Clear out job folder if it already exists and restart of the job was requested + if (programOptions.DeletePreviousJobOutput) + { + if (FileIOHelper.DeleteFolder(programOptions.OutputJobFolderPath) == false) + { + logger.Error("Unable to clear job folder {0}", programOptions.OutputJobFolderPath); + loggerConsole.Error("Unable to clear job folder {0}", programOptions.OutputJobFolderPath); + + return; + } + + // Sleep after deleting to let the file system catch up + Thread.Sleep(1000); + } + + logger.Info("Creating job output folder {0}", programOptions.OutputJobFolderPath); + loggerConsole.Info("Creating job output folder {0}", programOptions.OutputJobFolderPath); + + if (FileIOHelper.CreateFolder(programOptions.OutputJobFolderPath) == false) + { + logger.Error("Unable to create job output folder={0}", programOptions.OutputJobFolderPath); + loggerConsole.Error("Unable to create job output folder={0}", programOptions.OutputJobFolderPath); + + return; + } + + #endregion + logger.Trace("Executing:\r\n{0}", programOptions); loggerConsole.Trace("Executing:\r\n{0}", programOptions); loggerConsole.Warn("RunProgramCompare is not implemented yet"); + + // Go to work on the expanded and validated compare file } public static string ReadPassword(char mask) diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index f39701f..b873940 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.2.47.0")] -[assembly: AssemblyFileVersion("1.2.47.0")] +[assembly: AssemblyVersion("20.5.0.0")] +[assembly: AssemblyFileVersion("20.5.0.0")] diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json index 09dbcbd..b721b1b 100644 --- a/Properties/launchSettings.json +++ b/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "AppDynamics.Dexter.Core": { "commandName": "Project", - "commandLineArgs": "-j \"C:\\appdynamics\\AppDynamics.DEXTER\\JobFiles\\demodev.all.json\" -o D:\\AppD.Dexter.Out\\Demo\\ -v" + "commandLineArgs": "-j \"D:\\AppD.Dexter.Out\\Teranet\\Teranet.Polaris.json\" -o C:\\AppD.Dexter.Out -v -s" } } } \ No newline at end of file diff --git a/README.md b/README.md index f80bd0a..40875f2 100644 --- a/README.md +++ b/README.md @@ -91,9 +91,11 @@ Snapshot Exit Calls broken by time and duration: Snapshots with multiple Segments have an enhanced Waterfall view, with “^” caret character indicating exactly when in the Segment execution the Exit Calls occurred ![](../master/docs/introduction/SnapshotsTimelineWaterfall.png?raw=true) -An even better way to explore this data is by using PowerBI visual analytics tool with very interactive and rapid exploration views provided by [Snapshots in PowerBI](../../wiki/Snapshots-Report-in-PowerBI) and [Snapshots Method Calls in PowerBI](../../wiki/Snapshot-Method-Calls-Report-in-PowerBI) reports. +An even better way to explore this data is by using Tableau or PowerBI visual analytics tools with very interactive and rapid exploration views provided by [Snapshots in PowerBI](../../wiki/Snapshots-Report-in-PowerBI), [Snapshots Method Calls in PowerBI](../../wiki/Snapshot-Method-Calls-Report-in-PowerBI) reports and [Snapshots in PowerBI](../../wiki/Snapshots-Report-in-PowerBI) and [Snapshots in Tableau](../../wiki/Snapshot-Report-in-Tableau). -![Snapshots in PowerBI](../master/docs/introduction/SnaphotsPowerBI.png?raw=true) +![Snapshots in PowerBI](../master/docs/introduction/SnapshotsPowerBI.png?raw=true) + +![Snapshots in PowerBI](../master/docs/introduction/SnapshotsTableau.png.png?raw=true) You can even explore your Call Graph data: ![Snapshot Method Calls in PowerBI](../master/docs/introduction/SnapshotMethodCallsPowerBI.png?raw=true) diff --git a/ReportObjects/ActivityFlow/ActivityFlow.cs b/ReportObjects/ActivityFlow/ActivityFlow.cs index a67d16e..0a208b7 100644 --- a/ReportObjects/ActivityFlow/ActivityFlow.cs +++ b/ReportObjects/ActivityFlow/ActivityFlow.cs @@ -57,6 +57,8 @@ public class ActivityFlow public string MetricLink { get; set; } public List MetricsIDs { get; set; } + public bool IsCrossApplication { get; set; } + public ActivityFlow Clone() { return (ActivityFlow)this.MemberwiseClone(); diff --git a/ReportObjects/ActivityFlow/Maps/ApplicationActivityFlowReportMap.cs b/ReportObjects/ActivityFlow/Maps/ApplicationActivityFlowReportMap.cs index 0245370..18135ac 100644 --- a/ReportObjects/ActivityFlow/Maps/ApplicationActivityFlowReportMap.cs +++ b/ReportObjects/ActivityFlow/Maps/ApplicationActivityFlowReportMap.cs @@ -18,6 +18,7 @@ public ApplicationActivityFlowReportMap() Map(m => m.ToName).Index(i); i++; Map(m => m.ToType).Index(i); i++; Map(m => m.ToEntityID).Index(i); i++; + Map(m => m.IsCrossApplication).Index(i); i++; Map(m => m.ART).Index(i); i++; Map(m => m.Calls).Index(i); i++; Map(m => m.CPM).Index(i); i++; diff --git a/ReportObjects/ActivityFlow/Maps/BackendActivityFlowReportMap.cs b/ReportObjects/ActivityFlow/Maps/BackendActivityFlowReportMap.cs index 586e722..597229d 100644 --- a/ReportObjects/ActivityFlow/Maps/BackendActivityFlowReportMap.cs +++ b/ReportObjects/ActivityFlow/Maps/BackendActivityFlowReportMap.cs @@ -19,6 +19,7 @@ public BackendActivityFlowReportMap() Map(m => m.ToName).Index(i); i++; Map(m => m.ToType).Index(i); i++; Map(m => m.ToEntityID).Index(i); i++; + Map(m => m.IsCrossApplication).Index(i); i++; Map(m => m.ART).Index(i); i++; Map(m => m.Calls).Index(i); i++; Map(m => m.CPM).Index(i); i++; diff --git a/ReportObjects/ActivityFlow/Maps/BusinessTransactionActivityFlowReportMap.cs b/ReportObjects/ActivityFlow/Maps/BusinessTransactionActivityFlowReportMap.cs index 7c1af32..358ec88 100644 --- a/ReportObjects/ActivityFlow/Maps/BusinessTransactionActivityFlowReportMap.cs +++ b/ReportObjects/ActivityFlow/Maps/BusinessTransactionActivityFlowReportMap.cs @@ -20,6 +20,7 @@ public BusinessTransactionActivityFlowReportMap() Map(m => m.ToName).Index(i); i++; Map(m => m.ToType).Index(i); i++; Map(m => m.ToEntityID).Index(i); i++; + Map(m => m.IsCrossApplication).Index(i); i++; Map(m => m.ART).Index(i); i++; Map(m => m.Calls).Index(i); i++; Map(m => m.CPM).Index(i); i++; diff --git a/ReportObjects/ActivityFlow/Maps/NodeActivityFlowReportMap.cs b/ReportObjects/ActivityFlow/Maps/NodeActivityFlowReportMap.cs index 4cafd3e..4e8533c 100644 --- a/ReportObjects/ActivityFlow/Maps/NodeActivityFlowReportMap.cs +++ b/ReportObjects/ActivityFlow/Maps/NodeActivityFlowReportMap.cs @@ -20,6 +20,7 @@ public NodeActivityFlowReportMap() Map(m => m.ToName).Index(i); i++; Map(m => m.ToType).Index(i); i++; Map(m => m.ToEntityID).Index(i); i++; + Map(m => m.IsCrossApplication).Index(i); i++; Map(m => m.ART).Index(i); i++; Map(m => m.Calls).Index(i); i++; Map(m => m.CPM).Index(i); i++; diff --git a/ReportObjects/ActivityFlow/Maps/TierActivityFlowReportMap.cs b/ReportObjects/ActivityFlow/Maps/TierActivityFlowReportMap.cs index 3e5aa62..898fb17 100644 --- a/ReportObjects/ActivityFlow/Maps/TierActivityFlowReportMap.cs +++ b/ReportObjects/ActivityFlow/Maps/TierActivityFlowReportMap.cs @@ -19,6 +19,7 @@ public TierActivityFlowReportMap() Map(m => m.ToName).Index(i); i++; Map(m => m.ToType).Index(i); i++; Map(m => m.ToEntityID).Index(i); i++; + Map(m => m.IsCrossApplication).Index(i); i++; Map(m => m.ART).Index(i); i++; Map(m => m.Calls).Index(i); i++; Map(m => m.CPM).Index(i); i++; diff --git a/ReportObjects/FlameGraph/FoldedStackLine.cs b/ReportObjects/FlameGraph/FoldedStackLine.cs index aff2d73..c138b09 100644 --- a/ReportObjects/FlameGraph/FoldedStackLine.cs +++ b/ReportObjects/FlameGraph/FoldedStackLine.cs @@ -87,7 +87,6 @@ public FoldedStackLine(MethodCallLine methodCallLine, bool includeTimeOfEvent) if (methodCallLine.NumExits > 0) { // Encode the exits because they may contain carriage returns - string exitCallsWithTiming = methodCallLine.ExitCalls; Regex regexDuration = new Regex(@"\[\d+ ms( async)?\]", RegexOptions.IgnoreCase); diff --git a/VisualAnalytics/PowerBI/Design/EntityFlowmapsDesign.pbix b/VisualAnalytics/PowerBI/Design/EntityFlowmapsDesign.pbix index ed5c19a..9645dee 100644 Binary files a/VisualAnalytics/PowerBI/Design/EntityFlowmapsDesign.pbix and b/VisualAnalytics/PowerBI/Design/EntityFlowmapsDesign.pbix differ diff --git a/VisualAnalytics/PowerBI/Templates/EntityFlowmaps.pbit b/VisualAnalytics/PowerBI/Templates/EntityFlowmaps.pbit index 13a0251..54d340a 100644 Binary files a/VisualAnalytics/PowerBI/Templates/EntityFlowmaps.pbit and b/VisualAnalytics/PowerBI/Templates/EntityFlowmaps.pbit differ diff --git a/VisualAnalytics/Tableau/Design/Flowmaps.twb b/VisualAnalytics/Tableau/Design/Flowmaps.twb deleted file mode 100644 index e4b21cf..0000000 --- a/VisualAnalytics/Tableau/Design/Flowmaps.twb +++ /dev/null @@ -1,513 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - [APP.activitygrids.full.csv] - - Count - true - - "UTF-8" - "en_US" - "," - "true" - "en_US" - "" - - - - Controller - 129 - [Controller] - [APP.activitygrids.full.csv] - Controller - 0 - string - Count - 1 - 1073741823 - true - - - - ApplicationName - 129 - [ApplicationName] - [APP.activitygrids.full.csv] - ApplicationName - 1 - string - Count - 1 - 1073741823 - true - - - - CallType - 129 - [CallType] - [APP.activitygrids.full.csv] - CallType - 2 - string - Count - 1 - 1073741823 - true - - - - CallDirection - 129 - [CallDirection] - [APP.activitygrids.full.csv] - CallDirection - 3 - string - Count - 1 - 1073741823 - true - - - - FromName - 129 - [FromName] - [APP.activitygrids.full.csv] - FromName - 4 - string - Count - 1 - 1073741823 - true - - - - FromType - 129 - [FromType] - [APP.activitygrids.full.csv] - FromType - 5 - string - Count - 1 - 1073741823 - true - - - - FromEntityID - 20 - [FromEntityID] - [APP.activitygrids.full.csv] - FromEntityID - 6 - integer - Sum - true - - - ToName - 129 - [ToName] - [APP.activitygrids.full.csv] - ToName - 7 - string - Count - 1 - 1073741823 - true - - - - ToType - 129 - [ToType] - [APP.activitygrids.full.csv] - ToType - 8 - string - Count - 1 - 1073741823 - true - - - - ToEntityID - 20 - [ToEntityID] - [APP.activitygrids.full.csv] - ToEntityID - 9 - integer - Sum - true - - - ART - 20 - [ART] - [APP.activitygrids.full.csv] - ART - 10 - integer - Sum - true - - - Calls - 20 - [Calls] - [APP.activitygrids.full.csv] - Calls - 11 - integer - Sum - true - - - CPM - 20 - [CPM] - [APP.activitygrids.full.csv] - CPM - 12 - integer - Sum - true - - - Errors - 20 - [Errors] - [APP.activitygrids.full.csv] - Errors - 13 - integer - Sum - true - - - EPM - 20 - [EPM] - [APP.activitygrids.full.csv] - EPM - 14 - integer - Sum - true - - - ErrorsPercentage - 5 - [ErrorsPercentage] - [APP.activitygrids.full.csv] - ErrorsPercentage - 15 - real - Sum - true - - - From - 135 - [From] - [APP.activitygrids.full.csv] - From - 16 - datetime - Year - true - - - To - 135 - [To] - [APP.activitygrids.full.csv] - To - 17 - datetime - Year - true - - - FromUtc - 135 - [FromUtc] - [APP.activitygrids.full.csv] - FromUtc - 18 - datetime - Year - true - - - ToUtc - 135 - [ToUtc] - [APP.activitygrids.full.csv] - ToUtc - 19 - datetime - Year - true - - - Duration - 20 - [Duration] - [APP.activitygrids.full.csv] - Duration - 20 - integer - Sum - true - - - ApplicationID - 20 - [ApplicationID] - [APP.activitygrids.full.csv] - ApplicationID - 21 - integer - Sum - true - - - ControllerLink - 129 - [ControllerLink] - [APP.activitygrids.full.csv] - ControllerLink - 22 - string - Count - 1 - 1073741823 - true - - - - ApplicationLink - 129 - [ApplicationLink] - [APP.activitygrids.full.csv] - ApplicationLink - 23 - string - Count - 1 - 1073741823 - true - - - - FromLink - 129 - [FromLink] - [APP.activitygrids.full.csv] - FromLink - 24 - string - Count - 1 - 1073741823 - true - - - - ToLink - 129 - [ToLink] - [APP.activitygrids.full.csv] - ToLink - 25 - string - Count - 1 - 1073741823 - true - - - - MetricLink - 129 - [MetricLink] - [APP.activitygrids.full.csv] - MetricLink - 26 - string - Count - 1 - 1073741823 - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - [federated.1i7bjs61nwvek212xpuyg11xjzdd].[none:ApplicationName:nk] - [federated.1i7bjs61nwvek212xpuyg11xjzdd].[none:ExitType:nk] - - - - - - - ([federated.1i7bjs61nwvek212xpuyg11xjzdd].[none:Controller:nk] / ([federated.1i7bjs61nwvek212xpuyg11xjzdd].[none:ApplicationName:nk] / ([federated.1i7bjs61nwvek212xpuyg11xjzdd].[none:TierName:nk] / [federated.1i7bjs61nwvek212xpuyg11xjzdd].[sum:NumCalls:ok]))) - [federated.1i7bjs61nwvek212xpuyg11xjzdd].[sum:NumCalls:qk] -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - [federated.1i7bjs61nwvek212xpuyg11xjzdd].[avg:Duration:qk] - [federated.1i7bjs61nwvek212xpuyg11xjzdd].[sum:NumCalls:qk] -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - [federated.1i7bjs61nwvek212xpuyg11xjzdd].[Action (DurationRange,ExitType)] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ([federated.1i7bjs61nwvek212xpuyg11xjzdd].[cnt:SegmentID:qk] + [federated.1i7bjs61nwvek212xpuyg11xjzdd].[sum:Duration:qk]) - [federated.1i7bjs61nwvek212xpuyg11xjzdd].[tmi:Calculation_927460062514102273:qk] -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - [federated.1i7bjs61nwvek212xpuyg11xjzdd].[Action (MINUTE(OccurredTimeHourMinute),ExitType)] - - - - - - - - -
- -
-
- - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <formatted-text> + <run fontsize='10'>Number of Snapshots by User Experience in Controller/Application/Tier/Business Transactions</run> + </formatted-text> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:DurationRange:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:TierName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:AgentType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:HasErrors:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[Action (ApplicationName,BTName,Controller,Duration (bin),DurationRange,UserExperience)] + + + + + + + + + + + + + + + + + + + ([federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk]) + ([federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] * [federated.1git0tu0oszkk60zsjdbe0hyx003].[cnt:RequestID:qk]) +
+ +
+ + + + <formatted-text> + <run fontsize='10'>Histogram of Durations of Snapshots by User Experience in Controller/Application/Tier/Business Transactions</run> + </formatted-text> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:DurationRange:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:TierName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:AgentType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:HasErrors:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] + + + + + + + + + + + + + + + + + + + + (([federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk]) * [federated.1git0tu0oszkk60zsjdbe0hyx003].[cnt:Duration:qk]) + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Duration (bin):qk] + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Duration (bin):qk] + +
+ +
+ + + + <formatted-text> + <run fontsize='10'>Number of Snapshots by User Experience in Controller/Application/Tier/Business Transactions</run> + </formatted-text> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:DurationRange:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:TierName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:AgentType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:HasErrors:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] + + + + + + + + + + + + + + + + + + ([federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk]) + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] +
+ +
+ + + + <formatted-text> + <run fontsize='10'>Timeline of Snapshot Counts by User Experience in Controller/Application/Tier/Business Transactions</run> + </formatted-text> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:DurationRange:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:TierName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:AgentType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:HasErrors:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] + + + + + + + + + + + + + + + + + + + (([federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk]) * [federated.1git0tu0oszkk60zsjdbe0hyx003].[cnt:RequestID:qk]) + ([federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] / ([federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok])) +
+ +
+ + + + <formatted-text> + <run fontsize='10'>Gantt of Snapshot Counts by User Experience in Controller/Application/Tier/Business Transactions</run> + </formatted-text> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:DurationRange:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:TierName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:AgentType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:HasErrors:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] + + + + + + + + + + + + + + + + + ([federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] / ([federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk])) + ([federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] / ([federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok])) +
+ +
+ + + + <formatted-text> + <run fontsize='10'>Timeline of Snapshot Durations by User Experience in Controller/Application/Tier/Business Transactions</run> + </formatted-text> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:DurationRange:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:TierName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:AgentType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:HasErrors:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] + + + + + + + + + + + + + + + + + + + (([federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk]) * [federated.1git0tu0oszkk60zsjdbe0hyx003].[sum:Duration:qk]) + ([federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] / ([federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok])) +
+ +
+ + + + <formatted-text> + <run fontsize='10'>Timeline of Snapshot Durations by User Experience in Controller/Application/Tier/Business Transactions</run> + </formatted-text> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:DurationRange:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:TierName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:AgentType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:HasErrors:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] + + + + + + + + + + + + + + + + + + + + (([federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk]) * [federated.1git0tu0oszkk60zsjdbe0hyx003].[sum:Duration:qk]) + ([federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] / ([federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok])) +
+ +
+ + + + <formatted-text> + <run fontsize='10'>Timeline of Snapshot Counts and Durations by User Experience in Controller/Application/Tier/Business Transactions</run> + </formatted-text> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:DurationRange:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:TierName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:AgentType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:HasErrors:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[Action (ApplicationName,BTName,Controller,DurationRange,TierName,UserExperience)] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[Action (BTName,Controller,Duration (bin),UserExperience)] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[Action (BTName,BTType)] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[Action (ApplicationName,BTName,Controller,Duration (bin),DurationRange,UserExperience)] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (([federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk]) * ([federated.1git0tu0oszkk60zsjdbe0hyx003].[cnt:RequestID:qk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[sum:Duration:qk])) + ([federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] / ([federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok])) +
+ +
+ + + + <formatted-text> + <run fontsize='10'>Number of Snapshots vs Their Duration by Business Transaction Type</run> + </formatted-text> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:DurationRange:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:TierName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:AgentType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:HasErrors:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[Action (BTName,CallChains,Controller,DurationRange,DAY(Occurred),HOUR(Occurred),MINUTE(Occurred),UserExperience)] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[Action (BTName,Controller,Duration (bin),UserExperience)] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[Action (ApplicationName,BTName,Controller,Duration (bin),DurationRange,UserExperience)] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[avg:Duration:qk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[cnt:RequestID:qk] +
+ +
+ + + + <formatted-text> + <run fontsize='10'>Number of Snapshots by User Experience in Controller/Application/Tier/Business Transactions</run> + </formatted-text> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:DurationRange:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:TierName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:AgentType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:HasErrors:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <formatted-text> + <run fontsize="10">Number of Snapshots by User Experience in Controller/Application/Tier/Business Transactions</run> + </formatted-text> + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:DurationRange:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:TierName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:AgentType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:HasErrors:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[Action (ApplicationName,BTName,Controller,Duration (bin),DurationRange,UserExperience)] + + + + + + + + + + + + + + + + + + + ([federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk]) + ([federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] * [federated.1git0tu0oszkk60zsjdbe0hyx003].[cnt:RequestID:qk]) +
+ +
+ + + + <formatted-text> + <run fontsize="10">Histogram of Durations of Snapshots by User Experience in Controller/Application/Tier/Business Transactions</run> + </formatted-text> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:DurationRange:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:TierName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:AgentType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:HasErrors:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] + + + + + + + + + + + + + + + + + + + + (([federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk]) * [federated.1git0tu0oszkk60zsjdbe0hyx003].[cnt:Duration:qk]) + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Duration (bin):qk] + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Duration (bin):qk] + +
+ +
+ + + + <formatted-text> + <run fontsize="10">Number of Snapshots by User Experience in Controller/Application/Tier/Business Transactions</run> + </formatted-text> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:DurationRange:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:TierName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:AgentType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:HasErrors:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] + + + + + + + + + + + + + + + + + + ([federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk]) + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] +
+ +
+ + + + <formatted-text> + <run fontsize="10">Timeline of Snapshot Counts by User Experience in Controller/Application/Tier/Business Transactions</run> + </formatted-text> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:DurationRange:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:TierName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:AgentType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:HasErrors:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] + + + + + + + + + + + + + + + + + + + (([federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk]) * [federated.1git0tu0oszkk60zsjdbe0hyx003].[cnt:RequestID:qk]) + ([federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] / ([federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok])) +
+ +
+ + + + <formatted-text> + <run fontsize="10">Gantt of Snapshot Counts by User Experience in Controller/Application/Tier/Business Transactions</run> + </formatted-text> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:DurationRange:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:TierName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:AgentType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:HasErrors:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] + + + + + + + + + + + + + + + + + ([federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] / ([federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk])) + ([federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] / ([federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok])) +
+ +
+ + + + <formatted-text> + <run fontsize="10">Timeline of Snapshot Durations by User Experience in Controller/Application/Tier/Business Transactions</run> + </formatted-text> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:DurationRange:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:TierName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:AgentType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:HasErrors:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] + + + + + + + + + + + + + + + + + + + (([federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk]) * [federated.1git0tu0oszkk60zsjdbe0hyx003].[sum:Duration:qk]) + ([federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] / ([federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok])) +
+ +
+ + + + <formatted-text> + <run fontsize="10">Timeline of Snapshot Durations by User Experience in Controller/Application/Tier/Business Transactions</run> + </formatted-text> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:DurationRange:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:TierName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:AgentType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:HasErrors:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] + + + + + + + + + + + + + + + + + + + + (([federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk]) * [federated.1git0tu0oszkk60zsjdbe0hyx003].[sum:Duration:qk]) + ([federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] / ([federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok])) +
+ +
+ + + + <formatted-text> + <run fontsize="10">Timeline of Snapshot Counts and Durations by User Experience in Controller/Application/Tier/Business Transactions</run> + </formatted-text> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:DurationRange:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:TierName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:AgentType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:HasErrors:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[Action (ApplicationName,BTName,Controller,DurationRange,TierName,UserExperience)] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[Action (BTName,Controller,Duration (bin),UserExperience)] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[Action (BTName,BTType)] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[Action (ApplicationName,BTName,Controller,Duration (bin),DurationRange,UserExperience)] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (([federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk]) * ([federated.1git0tu0oszkk60zsjdbe0hyx003].[cnt:RequestID:qk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[sum:Duration:qk])) + ([federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] / ([federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] / [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok])) +
+ +
+ + + + <formatted-text> + <run fontsize="10">Number of Snapshots vs Their Duration by Business Transaction Type</run> + </formatted-text> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:DurationRange:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:TierName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:AgentType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:HasErrors:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[Action (BTName,CallChains,Controller,DurationRange,DAY(Occurred),HOUR(Occurred),MINUTE(Occurred),UserExperience)] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[Action (BTName,Controller,Duration (bin),UserExperience)] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[Action (ApplicationName,BTName,Controller,Duration (bin),DurationRange,UserExperience)] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[avg:Duration:qk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[cnt:RequestID:qk] +
+ +
+ + + + <formatted-text> + <run fontsize="10">Number of Snapshots by User Experience in Controller/Application/Tier/Business Transactions</run> + </formatted-text> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:ApplicationName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:DurationRange:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:TierName:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:Controller:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:AgentType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:BTType:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:HasErrors:nk] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[dy:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[hr:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[mi:Occurred:ok] + [federated.1git0tu0oszkk60zsjdbe0hyx003].[none:UserExperience:nk] + + + +