2857 lines
132 KiB
C#
2857 lines
132 KiB
C#
using C4IT.FASD.Base;
|
|
using C4IT.FileImport;
|
|
using C4IT.Logging;
|
|
using C4IT.Nexthink.NXQL;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using static C4IT.DataHistoryProvider.cNxqlDailyCollector;
|
|
using static C4IT.Logging.cLogManager;
|
|
|
|
namespace C4IT.DataHistoryProvider
|
|
{
|
|
public class cDataHistoryCollectorNxql : cDataHistoryCollectorModule
|
|
{
|
|
public const string constConnectorName = "Nexthink NXQL conntector";
|
|
public const string constLicenseId = "51EA7764-3AD3-4F90-89DB-DEB0C60D655C";
|
|
|
|
public cNxqlColumnsValidationResult LastValidationResults = null;
|
|
|
|
public cDataHistoryCollectorNxql(cDataHistoryCollector Collector) : base(Collector, enumDataHistoryOrigin.NexthinkNxql, constConnectorName, constLicenseId)
|
|
{
|
|
}
|
|
|
|
public static bool IsNxqlColumn(cDataHistoryConfigColumnBase Entry)
|
|
{
|
|
if (Entry is cDataHistoryConfigColumnNxtAction)
|
|
return true;
|
|
if (Entry is cDataHistoryConfigColumnNxtCampaign)
|
|
return true;
|
|
if (Entry is cDataHistoryConfigColumnNxtCategory)
|
|
return true;
|
|
if (Entry is cDataHistoryConfigColumnNxtScore)
|
|
return true;
|
|
if (Entry is cDataHistoryConfigColumnNxtCompute)
|
|
return true;
|
|
if (Entry is cDataHistoryConfigColumn)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
private static string GetTimeFilter(DateTime? time, bool isLeft)
|
|
{
|
|
if (time == null)
|
|
return "now";
|
|
|
|
var time2 = (DateTime)time;
|
|
time2 = time2.AddTicks(-(time2.Ticks % 10000000));
|
|
|
|
if (isLeft)
|
|
time2.AddSeconds(-1);
|
|
|
|
string strFrom = string.Format("{0:yyyy-MM-dd HH:mm:ss}", time2);
|
|
strFrom = strFrom.Replace(' ', '@');
|
|
return strFrom;
|
|
}
|
|
|
|
private static string GetTimeFilter(DateTime? From, DateTime? To)
|
|
{
|
|
var strFrom = GetTimeFilter(From, true);
|
|
var strTo = GetTimeFilter(To, false);
|
|
return string.Format("(between {0} {1})", strFrom, strTo);
|
|
}
|
|
|
|
private static string GetNxqlColumnName(string Column, bool isCustom)
|
|
{
|
|
var RV = Column;
|
|
if (isCustom)
|
|
{
|
|
RV = Column.Replace("\"", "\"\"");
|
|
RV = $"#\"{RV}\"";
|
|
}
|
|
return RV;
|
|
}
|
|
|
|
public static string GetNxqlColumnName(cDataHistoryConfigColumnBase Entry)
|
|
{
|
|
var strCol = GetNxqlColumnName2(Entry, true);
|
|
if (!string.IsNullOrEmpty(Entry.SourceTable))
|
|
strCol = $"({Entry.SourceTable} {strCol})";
|
|
|
|
return strCol;
|
|
}
|
|
|
|
public static string GetNxqlColumnResultName(cDataHistoryConfigColumnBase Entry)
|
|
{
|
|
var strCol = GetNxqlColumnName2(Entry, false).ToLowerInvariant();
|
|
if (strCol.StartsWith("*"))
|
|
strCol = strCol.Remove(0, 1);
|
|
if (!string.IsNullOrEmpty(Entry.SourceTable))
|
|
strCol = $"{Entry.SourceTable.ToLowerInvariant()}/{strCol}";
|
|
|
|
return strCol;
|
|
}
|
|
|
|
private static string GetNxqlColumnName2(cDataHistoryConfigColumnBase Entry, bool withCustom)
|
|
{
|
|
if (Entry is cDataHistoryConfigColumnNxtAction _ac)
|
|
{
|
|
return GetNxqlColumnName($"action:{_ac.ActionName}/{_ac.ValueName}", withCustom);
|
|
}
|
|
else if (Entry is cDataHistoryConfigColumnNxtScore _sc)
|
|
{
|
|
return GetNxqlColumnName($"score:{_sc.ScoreName}/{_sc.ValueName}{(_sc.IsPayload ? "/payload" : "")}", withCustom);
|
|
}
|
|
else if (Entry is cDataHistoryConfigColumnNxtCategory _ca)
|
|
{
|
|
return GetNxqlColumnName(_ca.SourceName, withCustom);
|
|
}
|
|
else if (Entry is cDataHistoryConfigColumnNxtCampaign _cn)
|
|
{
|
|
var strIsText = "";
|
|
if (_cn.AsText)
|
|
strIsText = " (text)";
|
|
return GetNxqlColumnName($"campaign:{_cn.CampaignName}/{_cn.ValueName}{strIsText}", withCustom);
|
|
}
|
|
else if (Entry is cDataHistoryConfigColumnNxtCompute _cp)
|
|
{
|
|
return GetNxqlColumnName(_cp.SourceName, false);
|
|
}
|
|
else if (Entry is cDataHistoryConfigColumn _co)
|
|
{
|
|
return GetNxqlColumnName(_co.SourceName, false);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static string getNxqlStatement(string NxqlQuery, enumFasdInformationClass Class, List<object> KeyValues, List<cDataHistoryConfigColumnBase> KeyColumns, uint Limit, DateTime? From, DateTime? To, List<cDataHistoryConfigColumnBase> Columns, string TableName)
|
|
{
|
|
try
|
|
{
|
|
var strLimit = "";
|
|
if (Limit >= 0)
|
|
strLimit = $"(limit {Limit})";
|
|
|
|
var strTimeFilter = GetTimeFilter(From, To);
|
|
|
|
var strKeyFilter = "";
|
|
if (KeyColumns != null && KeyValues != null && KeyValues.Count > 0)
|
|
{
|
|
var keyVal = Convert.ToInt64(KeyValues[0]);
|
|
var tbn = "device";
|
|
if (KeyColumns[0]?.ParentTable?.ParentTable?.ParentCluster?.InformationClass == enumFasdInformationClass.User)
|
|
tbn = "user";
|
|
strKeyFilter = $"(where {tbn} (eq id (identifier \"{keyVal}\")))";
|
|
}
|
|
|
|
var strColumns = "";
|
|
var strCompus = "";
|
|
var cols = new HashSet<string>();
|
|
foreach (var Entry in Columns)
|
|
{
|
|
if (Entry is cDataHistoryConfigColumn _col && _col.SourceType == eDataHistoryQueryType.Static)
|
|
continue;
|
|
|
|
string strCol = GetNxqlColumnName(Entry);
|
|
if (strCol == null)
|
|
continue;
|
|
if (cols.Contains(strCol))
|
|
continue;
|
|
cols.Add(strCol);
|
|
if (Entry is cDataHistoryConfigColumnNxtCompute)
|
|
{
|
|
if (strCompus != "")
|
|
strCompus += " ";
|
|
strCompus += strCol;
|
|
}
|
|
else
|
|
{
|
|
if (strColumns != "")
|
|
strColumns += " ";
|
|
strColumns += strCol;
|
|
}
|
|
}
|
|
|
|
if (strColumns == "")
|
|
{
|
|
cDataHistoryConfigColumnBase firstCol = null;
|
|
if (KeyColumns != null)
|
|
foreach (var Col in KeyColumns)
|
|
{
|
|
if (Col is cDataHistoryConfigColumn _c && _c.SourceType == eDataHistoryQueryType.Static)
|
|
continue;
|
|
firstCol = Col;
|
|
break;
|
|
}
|
|
|
|
if (firstCol == null)
|
|
{
|
|
LogEntry($"The NXQL query with table name {TableName} has no valid columns nor a valid key column", LogLevels.Warning);
|
|
return null;
|
|
}
|
|
|
|
strColumns = GetNxqlColumnName(firstCol);
|
|
}
|
|
strColumns = "(" + strColumns + ")";
|
|
if (!string.IsNullOrEmpty(strCompus))
|
|
strCompus = $"(compute {strCompus})";
|
|
|
|
var RetVal = NxqlQuery.Replace("#Columns#", strColumns);
|
|
RetVal = RetVal.Replace("#KeyFilter#", strKeyFilter);
|
|
RetVal = RetVal.Replace("#Limit#", strLimit);
|
|
RetVal = RetVal.Replace("#TimeFilter#", strTimeFilter);
|
|
RetVal = RetVal.Replace("#Compute#", strCompus);
|
|
|
|
return RetVal;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public override async Task<List<object>> GetIds(cF4sdConnectorIds IdEntry, enumFasdInformationClass InfoClass, cF4sdWebRequestInfo requestInfo, int LogDeep)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
|
|
var _startTime = DateTime.UtcNow;
|
|
|
|
var _retVal = new List<object>();
|
|
|
|
try
|
|
{
|
|
Dictionary<string, object> Params;
|
|
string QuerName;
|
|
switch (InfoClass)
|
|
{
|
|
case enumFasdInformationClass.Computer:
|
|
Params = new Dictionary<string, object>() { { "NAME", IdEntry.name.ToUpperInvariant() } };
|
|
QuerName = "FindNxtIdsComputer";
|
|
break;
|
|
case enumFasdInformationClass.User:
|
|
Params = new Dictionary<string, object>() { { "SID", IdEntry.sid } };
|
|
QuerName = "FindNxtIdsUser";
|
|
break;
|
|
default:
|
|
return _retVal;
|
|
}
|
|
|
|
if (!DataHistorySqlHelper.GetWellKnownSqlStatement(QuerName, out var Query))
|
|
return _retVal;
|
|
|
|
var sqlerror = 0;
|
|
var sqlStartTime = DateTime.UtcNow;
|
|
|
|
|
|
try
|
|
{
|
|
using (var Conn = new cDbConnection(Collector.mainDbConnection))
|
|
{
|
|
if (!Conn.IsOpen)
|
|
{
|
|
LogEntry($"Could not open main sql database '{Collector.mainDbConnection.Database}', aborting saving NXQL column validation result", LogLevels.Error);
|
|
return _retVal;
|
|
}
|
|
|
|
using (var Reader = await DataHistorySqlHelper.GetTableResultAsync(Conn, Query, Params, CancellationToken.None))
|
|
{
|
|
if (Reader == null)
|
|
return _retVal;
|
|
|
|
try
|
|
{
|
|
if (Reader.HasRows)
|
|
{
|
|
if (await Reader.ReadAsync())
|
|
{
|
|
try
|
|
{
|
|
var Engine = Reader.GetString(0);
|
|
var NxtId = Reader.GetInt64(1);
|
|
|
|
return new List<object>() { Engine, NxtId };
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
cLogManager.DefaultLogger.LogException(E);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
cLogManager.DefaultLogger.LogException(E);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
sqlerror = E.HResult;
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (DataHistorySqlHelper.LogSql) DataHistorySqlHelper.SaveSqlTimingEntry(Query.Name, sqlStartTime, Params, sqlerror, requestInfo?.requestName);
|
|
}
|
|
|
|
using (var Conn = new cDbConnection(Collector.mainDbConnection))
|
|
{
|
|
if (!Conn.IsOpen)
|
|
{
|
|
LogEntry($"Could not open main sql database '{Collector.mainDbConnection.Database}', aborting saving NXQL column validation result", LogLevels.Error);
|
|
return _retVal;
|
|
}
|
|
|
|
using (var Reader = await DataHistorySqlHelper.GetTableResultAsync(Conn, Query, Params, CancellationToken.None))
|
|
{
|
|
if (Reader == null)
|
|
return _retVal;
|
|
|
|
try
|
|
{
|
|
if (Reader.HasRows)
|
|
{
|
|
if (await Reader.ReadAsync())
|
|
{
|
|
try
|
|
{
|
|
var Engine = Reader.GetString(0);
|
|
var NxtId = Reader.GetInt64(1);
|
|
|
|
return new List<object>() { Engine, NxtId };
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
cLogManager.DefaultLogger.LogException(E);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
cLogManager.DefaultLogger.LogException(E);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return _retVal;
|
|
}
|
|
|
|
public override async Task<cScanTimeInfo> GetScanTimeInfoAsync(cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
|
|
{
|
|
return await GetScanTimeInfoAsync(Collector.InfrastructureConfig.Nexthink.ScanTiming, "NxqlScan-all", requestInfo, LogDeep, Token);
|
|
}
|
|
|
|
public override void TablePostConfig(cDataHistoryConfigTable Table)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
try
|
|
{
|
|
if (Table.Cached == eDataHistoryTableCached.Default)
|
|
{
|
|
if (Table.Type != eDataHistoryTableType.Static)
|
|
Table.Cached = eDataHistoryTableCached.Yes;
|
|
else
|
|
Table.Cached = eDataHistoryTableCached.No;
|
|
}
|
|
Table.IsVirtual = true;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
}
|
|
|
|
public override async Task<bool> DoScanAsync(bool Always, bool Rescan, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token, bool EnhancedDebug = false)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
|
|
var _startTime = DateTime.UtcNow;
|
|
|
|
var ShowFinished = false;
|
|
try
|
|
{
|
|
await Task.Run<bool>(async () =>
|
|
{
|
|
try
|
|
{
|
|
Collector.DoProcessUiMessage(0, "");
|
|
|
|
var scanInfo = await GetScanTimeInfoAsync(requestInfo, LogDeep + 1, Token);
|
|
if (scanInfo != null)
|
|
{
|
|
if (scanInfo.NextScan == null)
|
|
{
|
|
Collector.DoProcessUiMessage(0, "not valid next scan time info for NXQL connector");
|
|
return false;
|
|
}
|
|
|
|
var nextScan = (DateTime)scanInfo.NextScan;
|
|
if (!(scanInfo.LastScan is DateTime lastScan) || (lastScan >= nextScan))
|
|
{
|
|
Collector.DoProcessUiMessage(0, $"currently no scan needed for NXQL connector, lastScan={scanInfo.LastScan} (UTC), nextScan={scanInfo.NextScan} (UTC)");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!Always)
|
|
{
|
|
Collector.DoProcessUiMessage(0, "no valid NXQL scan done yet, please start a scan manually.");
|
|
return false;
|
|
}
|
|
|
|
scanInfo = new cScanTimeInfo(DateTime.MinValue);
|
|
scanInfo.GetFirstScanTime(Collector.InfrastructureConfig.Nexthink.ScanTiming);
|
|
}
|
|
|
|
ShowFinished = true;
|
|
Collector.DoProcessUiMessage(0, "Starting NXQL scan...");
|
|
Collector.DoProcessUiMessage(0, "");
|
|
|
|
if (LastValidationResults == null)
|
|
{
|
|
Collector.DoProcessUiMessage(0, "Starting NXQL column validation...");
|
|
Collector.DoProcessUiMessage(0, "");
|
|
var ColumnsValidator = new cNxqlColumnValidator(Collector);
|
|
await ColumnsValidator.DoValidationAsync(requestInfo, LogDeep + 1, Token).ConfigureAwait(false);
|
|
LastValidationResults = ColumnsValidator.ValidationResults;
|
|
Collector.DoProcessUiMessage(0, "NXQL column validation finished");
|
|
Collector.DoProcessUiMessage(0, "");
|
|
}
|
|
if (LastValidationResults == null)
|
|
return false;
|
|
|
|
foreach (var ScanInterval in scanInfo.Intervals)
|
|
{
|
|
if (Token.IsCancellationRequested)
|
|
return false;
|
|
|
|
await DoScanIntervalAsync("NxqlScan-all", ScanInterval.Key, ScanInterval.Value, requestInfo, LogDeep + 1, Token, EnhancedDebug);
|
|
}
|
|
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
|
|
return false;
|
|
});
|
|
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
Collector.DoProcessUiMessage(0, "");
|
|
if (ShowFinished)
|
|
Collector.DoProcessUiMessage(0, "NXQL scan finished.");
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public async Task<bool> DoScanIntervalAsync(string ScanName, DateTime FromTime, DateTime ToTime, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token, bool EnhancedDebug)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
|
|
var _startTime = DateTime.UtcNow;
|
|
|
|
try
|
|
{
|
|
// create the scan history entry and get the id
|
|
var ScanId = await Collector.CreateScanHistoryEntry(ScanName, FromTime, ToTime, requestInfo, LogDeep + 1, Token);
|
|
|
|
if (Token.IsCancellationRequested)
|
|
return false;
|
|
|
|
var Validator = new cNxqlDataCollector(Collector, LastValidationResults, ScanId, EnhancedDebug);
|
|
Validator.CreateJobs(FromTime, ToTime, requestInfo, LogDeep + 1);
|
|
|
|
await Validator.Orchestrator.ProcessAsync(CancellationToken.None).ConfigureAwait(false);
|
|
|
|
if (Token.IsCancellationRequested)
|
|
return false;
|
|
|
|
var _sum = Validator.validResultCount + Validator.validResultCount;
|
|
if (_sum > 0 && (Validator.errorResultCount * 1000 / _sum) > 100)
|
|
{
|
|
LogEntry($"To many invalid NXQL scan to complete scan (valid: {Validator.validResultCount}, errors: {Validator.validResultCount}.", LogLevels.Error);
|
|
return false;
|
|
}
|
|
|
|
await Collector.ConfirmScanHistoryEntry(ScanId, requestInfo, LogDeep + 1, Token);
|
|
|
|
return true;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
public override bool CheckIfLateDelivery(cDataHistoryConfigTable Table)
|
|
{
|
|
return Table.Cached == eDataHistoryTableCached.Yes;
|
|
}
|
|
|
|
private async Task GetTableValuesSqlAsync(List<Task<List<cF4SDHealthCardRawData.cHealthCardTable>>> lstTasks, Dictionary<string, cF4SDHealthCardRawData.cHealthCardTable> RetVal, cF4sdWebRequestInfo requestInfo, int LogDeep)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
|
|
var _startTime = DateTime.UtcNow;
|
|
|
|
try
|
|
{
|
|
var Result = await Task.WhenAll(lstTasks).ConfigureAwait(false);
|
|
if (Result != null)
|
|
{
|
|
LogEntry($"SQL task result for NXQL tables: {Result.Length}", LogLevels.Debug);
|
|
foreach (var Entry in Result)
|
|
{
|
|
if (Entry != null)
|
|
{
|
|
if (Entry.Count == 0)
|
|
LogEntry($"No table result for SQL task result for NXQL tables.", LogLevels.Debug);
|
|
foreach (var Entry2 in Entry)
|
|
RetVal[Entry2.Name] = Entry2;
|
|
}
|
|
else
|
|
LogEntry($"Empty result in SQL task result for NXQL tables.", LogLevels.Warning);
|
|
}
|
|
}
|
|
else
|
|
LogEntry($"Could not get a valid SQL result for the NXQL tables", LogLevels.Warning);
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
}
|
|
|
|
private async Task<Dictionary<string, cTableResult>> GetTableValuesNxqlAsync(List<object> ComputerIds, List<object> UserIds, List<cDataHistoryConfigTable> lstTables, DateTime TodayFrom, DateTime TodayTo, CancellationToken Token, cF4sdWebRequestInfo requestInfo, int LogDeep)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
|
|
var _startTime = DateTime.UtcNow;
|
|
|
|
try
|
|
{
|
|
if ((ComputerIds != null && ComputerIds.Count >= 2) ||
|
|
(UserIds != null && UserIds.Count >= 2))
|
|
{
|
|
var ColVaidation = new cNxqlColumnsValidationResult();
|
|
await ColVaidation.LoadFromSqlAsync(Collector, Token, requestInfo, LogDeep + 1);
|
|
|
|
if (ColVaidation == null || ColVaidation.Count == 0)
|
|
LogEntry($"No valid column validation entries found.", LogLevels.Warning);
|
|
|
|
var NxqlCollector = new cNxqlDailyCollector(Collector, ColVaidation);
|
|
|
|
if ((ComputerIds != null && ComputerIds.Count >= 2))
|
|
NxqlCollector.CreateJobs(lstTables, TodayFrom, TodayTo, ComputerIds[0].ToString(), Convert.ToInt64(ComputerIds[1]), enumFasdInformationClass.Computer);
|
|
if ((UserIds != null && UserIds.Count >= 2))
|
|
NxqlCollector.CreateJobs(lstTables, TodayFrom, TodayTo, UserIds[0].ToString(), Convert.ToInt64(UserIds[1]), enumFasdInformationClass.User);
|
|
|
|
await NxqlCollector.Orchestrator.ProcessAsync(Token);
|
|
return NxqlCollector.Results;
|
|
}
|
|
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public override async Task<List<cF4SDHealthCardRawData.cHealthCardTable>> GetTableResultsVirtualAsync(List<cDataHistoryConfigTable> Tables, Dictionary<enumFasdInformationClass, cF4sdConnectorIds> Identities, DateTime RefTime, int MaxAge, bool instantly, Guid? CacheId, CancellationToken Token, cF4sdWebRequestInfo requestInfo, int LogDeep)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
|
|
var _startTime = DateTime.UtcNow;
|
|
|
|
try
|
|
{
|
|
var lstNxqlTables = new List<cDataHistoryConfigTable>();
|
|
foreach (var Table in Tables)
|
|
{
|
|
if (!this.Origins.Contains(Table.ParentCluster.Origin))
|
|
continue;
|
|
|
|
lstNxqlTables.Add(Table);
|
|
}
|
|
if (lstNxqlTables.Count == 0)
|
|
return null;
|
|
|
|
if (!instantly && CacheId == null)
|
|
{
|
|
var RetValCached = new List<cF4SDHealthCardRawData.cHealthCardTable>();
|
|
foreach (var Table in lstNxqlTables)
|
|
{
|
|
RetValCached.Add(new cF4SDHealthCardRawData.cHealthCardTable()
|
|
{
|
|
Name = Table.Name,
|
|
InformationClass = Table.ParentCluster.InformationClass,
|
|
Origin = Table.ParentCluster.Origin,
|
|
IsIncomplete = true
|
|
}); ;
|
|
}
|
|
|
|
return RetValCached;
|
|
}
|
|
|
|
List<object> ComputerIds = null;
|
|
List<object> UserIds = null;
|
|
|
|
var lstTasks = new List<Task<List<cF4SDHealthCardRawData.cHealthCardTable>>>(Tables.Count);
|
|
var lstTables = new List<cDataHistoryConfigTable>();
|
|
|
|
var TodayTo = DateTime.UtcNow;
|
|
var _sci = new cScanTimeInfo(TodayTo);
|
|
var TodayFrom = _sci.GetScanTime(TodayTo, Collector.InfrastructureConfig.Nexthink.ScanTiming);
|
|
|
|
LogEntry($"Todays timeframe: {TodayFrom} - {TodayTo}", LogLevels.Debug);
|
|
|
|
foreach (var Table in lstNxqlTables)
|
|
{
|
|
List<object> lstFilter;
|
|
|
|
switch (Table.ParentCluster.InformationClass)
|
|
{
|
|
case enumFasdInformationClass.Computer:
|
|
if (ComputerIds == null)
|
|
if (Identities.TryGetValue(enumFasdInformationClass.Computer, out var idsComputer))
|
|
ComputerIds = await this.GetIds(idsComputer, enumFasdInformationClass.Computer, requestInfo, LogDeep + 1);
|
|
lstFilter = ComputerIds;
|
|
if (!lstTables.Contains(Table))
|
|
lstTables.Add(Table);
|
|
break;
|
|
case enumFasdInformationClass.User:
|
|
if (UserIds == null)
|
|
if (Identities.TryGetValue(enumFasdInformationClass.User, out var idsUser))
|
|
UserIds = await this.GetIds(idsUser, enumFasdInformationClass.User, requestInfo, LogDeep + 1);
|
|
lstFilter = UserIds;
|
|
if (!lstTables.Contains(Table))
|
|
lstTables.Add(Table);
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
if (!(lstFilter?.Count >= 2))
|
|
{
|
|
LogEntry($"No valid filter properties for NXQL table '{Table?.Name}'", LogLevels.Warning);
|
|
continue;
|
|
}
|
|
|
|
var Filter = new Dictionary<string, object>(2) { { "engine", lstFilter[0] }, { "id_int", lstFilter[1] } };
|
|
lstTasks.Add(Collector.GetCachedHistoricTable(Table, Filter, RefTime, MaxAge, Collector.InfrastructureConfig.Nexthink.ScanTiming, Token, requestInfo, LogDeep + 1));
|
|
}
|
|
|
|
object ComputerId1 = null;
|
|
object ComputerId2 = null;
|
|
if (ComputerIds != null)
|
|
{
|
|
ComputerId1 = ComputerIds.Count >= 1 ? ComputerIds[0] : null;
|
|
ComputerId2 = ComputerIds.Count >= 1 ? ComputerIds[1] : null;
|
|
}
|
|
LogEntry($"Computer IDs: {ComputerId1}, {ComputerId2}", LogLevels.Debug);
|
|
|
|
object UserId1 = null;
|
|
object UserId2 = null;
|
|
if (UserIds != null)
|
|
{
|
|
UserId1 = UserIds.Count >= 1 ? UserIds[0] : null;
|
|
UserId2 = UserIds.Count >= 1 ? UserIds[1] : null;
|
|
}
|
|
LogEntry($"User IDs: {UserId1}, {UserId2}", LogLevels.Debug);
|
|
|
|
var RetVal = new Dictionary<string, cF4SDHealthCardRawData.cHealthCardTable>();
|
|
Dictionary<string, cTableResult> DailyResult = null;
|
|
|
|
var tsk1 = Task.Run(async () =>
|
|
{
|
|
await GetTableValuesSqlAsync(lstTasks, RetVal, requestInfo, LogDeep + 1);
|
|
});
|
|
|
|
var tsk2 = Task.Run(async () =>
|
|
{
|
|
DailyResult = await GetTableValuesNxqlAsync(ComputerIds, UserIds, lstTables, TodayFrom, TodayTo, Token, requestInfo, LogDeep + 1);
|
|
});
|
|
|
|
await Task.WhenAll(new Task[] { tsk1, tsk2 });
|
|
|
|
if (!tsk1.IsCompleted)
|
|
LogEntry("The SQL task results for NXQL tables did not complete.", LogLevels.Warning);
|
|
if (!tsk2.IsCompleted)
|
|
LogEntry("The todays NXQL task results for NXQL tables did not complete.", LogLevels.Warning);
|
|
|
|
if (DailyResult == null)
|
|
return null;
|
|
|
|
foreach (var DailyResultEntry in DailyResult)
|
|
{
|
|
try
|
|
{
|
|
var TableName = DailyResultEntry.Key;
|
|
var DailyValues = DailyResultEntry.Value;
|
|
|
|
if (DefaultLogger.IsDebug) LogEntry($"Including daily values in NXQL result for table '{TableName}'.", LogLevels.Debug);
|
|
|
|
if (RetVal.TryGetValue(TableName, out var SqlValues))
|
|
{
|
|
if (DefaultLogger.IsDebug) LogEntry($"Including daily values in existing SQL result for '{TableName}'.", LogLevels.Debug);
|
|
|
|
if (SqlValues.StartingIndex == int.MaxValue)
|
|
SqlValues.StartingIndex = 1;
|
|
|
|
foreach (var DailyValueEntry in DailyValues)
|
|
{
|
|
if (SqlValues.Columns.TryGetValue(DailyValueEntry.Key, out var SqlValueEntry))
|
|
{
|
|
if (SqlValues.StartingIndex > 0)
|
|
{
|
|
for (int i = 0; i < SqlValues.StartingIndex - 1; i++)
|
|
SqlValueEntry.Values.Insert(0, null);
|
|
SqlValueEntry.Values.Insert(0, DailyValueEntry.Value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SqlValueEntry = new cF4SDHealthCardRawData.cHealthCardTableColumn(SqlValues)
|
|
{
|
|
ColumnName = DailyValueEntry.Key,
|
|
Values = new List<object>(1) { DailyValueEntry.Value }
|
|
};
|
|
SqlValues.Columns.Add(DailyValueEntry.Key, SqlValueEntry);
|
|
}
|
|
}
|
|
|
|
foreach (var SqlValueEntry in SqlValues.Columns.Values)
|
|
{
|
|
if (DailyValues.ContainsKey(SqlValueEntry.ColumnName))
|
|
continue;
|
|
|
|
if (SqlValues.StartingIndex > 0)
|
|
{
|
|
for (int i = 0; i < SqlValues.StartingIndex - 1; i++)
|
|
SqlValueEntry.Values.Insert(0, null);
|
|
SqlValueEntry.Values.Insert(0, null);
|
|
}
|
|
}
|
|
|
|
if (DefaultLogger.IsDebug) LogEntry($"Including timeframe in existing SQL result for '{TableName}'.", LogLevels.Debug);
|
|
if (SqlValues.StartingIndex > 0 && SqlValues.TimeFrames != null)
|
|
{
|
|
try
|
|
{
|
|
var lstTimeframes = new List<KeyValuePair<DateTime, DateTime>>
|
|
{
|
|
new KeyValuePair<DateTime, DateTime>(TodayFrom, TodayTo)
|
|
};
|
|
for (int i = 0; i < SqlValues.StartingIndex - 1; i++)
|
|
lstTimeframes.Add(new KeyValuePair<DateTime, DateTime>(DateTime.MinValue, DateTime.MinValue));
|
|
for (int j = 0; j < SqlValues.TimeFrames.Length / 2; j++)
|
|
lstTimeframes.Add(new KeyValuePair<DateTime, DateTime>(SqlValues.TimeFrames[j, 0], SqlValues.TimeFrames[j, 1]));
|
|
SqlValues.TimeFrames = new DateTime[lstTimeframes.Count, 2];
|
|
for (int i = 0; i < lstTimeframes.Count; i++)
|
|
{
|
|
SqlValues.TimeFrames[i, 0] = lstTimeframes[i].Key;
|
|
SqlValues.TimeFrames[i, 1] = lstTimeframes[i].Value;
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
}
|
|
|
|
SqlValues.StartingIndex = Math.Min(SqlValues.StartingIndex, 0);
|
|
}
|
|
else
|
|
{
|
|
if (DefaultLogger.IsDebug) LogEntry($"No valid SQL data found for NXQL results for table '{TableName}', creating a new table result with todays values only", LogLevels.Debug);
|
|
var TableResult = new cF4SDHealthCardRawData.cHealthCardTable()
|
|
{
|
|
Name = TableName,
|
|
InformationClass = DailyValues.InformationClass,
|
|
Origin = enumDataHistoryOrigin.NexthinkNxql,
|
|
IsIncomplete = true,
|
|
IsStatic = false,
|
|
TableType = eDataHistoryTableType.History,
|
|
StartingIndex = 0,
|
|
TimeFrames = new DateTime[1, 2] { { TodayFrom, TodayTo } },
|
|
Columns = new Dictionary<string, cF4SDHealthCardRawData.cHealthCardTableColumn>()
|
|
};
|
|
|
|
foreach (var ColEntry in DailyValues)
|
|
{
|
|
var _col = new cF4SDHealthCardRawData.cHealthCardTableColumn(TableResult)
|
|
{
|
|
ColumnName = ColEntry.Key,
|
|
Values = new List<object>(1) { ColEntry.Value }
|
|
};
|
|
TableResult.Columns[_col.ColumnName] = _col;
|
|
}
|
|
|
|
RetVal[TableName] = TableResult;
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
}
|
|
|
|
if (RetVal.Count > 0)
|
|
return RetVal.Values.ToList();
|
|
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
}
|
|
|
|
public class cNxqlColumnValidator
|
|
{
|
|
public readonly cDataHistoryCollector Collector;
|
|
public readonly cNxqlOrchestration Orchestrator;
|
|
|
|
public readonly cNxqlColumnsValidationResult ValidationResults = new cNxqlColumnsValidationResult();
|
|
|
|
public cNxqlColumnValidator(cDataHistoryCollector Collector)
|
|
{
|
|
this.Collector = Collector;
|
|
Orchestrator = new cNxqlOrchestration("ColumnValidator");
|
|
}
|
|
|
|
public int SetNxqlJobsTables()
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
try
|
|
{
|
|
var QueryCount = 0;
|
|
foreach (var Portal in Collector.InfrastructureConfig.Nexthink.Portals.Values)
|
|
{
|
|
var lstServer = Portal.Engines.Values.ToList();
|
|
if (lstServer.Count == 0)
|
|
continue;
|
|
|
|
var PortalResult = new cNxqlPortalValidationResult();
|
|
var indexServer = 0;
|
|
foreach (var Table in Collector.ClusterConfig.Tables.Values)
|
|
{
|
|
if (Table.ParentCluster.Origin != enumDataHistoryOrigin.NexthinkNxql)
|
|
continue;
|
|
|
|
foreach (var SubTable in Table.SubTables.Values)
|
|
{
|
|
if (!(SubTable.Template is cDataHistoryConfigQueryTemplateNxql NxqlTemplate))
|
|
continue;
|
|
|
|
bool isValidTable = true;
|
|
foreach (var Column in SubTable.ParentTable.KeyColumns)
|
|
{
|
|
if (!cDataHistoryCollectorNxql.IsNxqlColumn(Column) && (Column is cDataHistoryConfigColumn _C && _C.SourceType != eDataHistoryQueryType.Static))
|
|
isValidTable = false;
|
|
}
|
|
|
|
if (!isValidTable)
|
|
{
|
|
AddResultTable(Portal, SubTable, false, "", "the table key columns are not valid.");
|
|
}
|
|
else
|
|
{
|
|
var Server = lstServer[indexServer];
|
|
|
|
var strNxqlQuery = cDataHistoryCollectorNxql.getNxqlStatement(NxqlTemplate.Query, Table.ParentCluster.InformationClass, null, Table.KeyColumns, 0, null, null, SubTable.ParentTable.KeyColumns, SubTable.Name);
|
|
if (string.IsNullOrEmpty(strNxqlQuery))
|
|
AddResultTable(Portal, SubTable, false, "", "could not get a valid NXQL statement for queriing the key columns.");
|
|
else
|
|
{
|
|
var Job = new cNxqlColumnValidatorTableJob(this, SubTable, Portal, strNxqlQuery);
|
|
Orchestrator.AddJob($"{Portal.Name}|{SubTable.Name}", Server.Address, strNxqlQuery, Portal.Credential, Port: Server.Port.ToString(), PreProcessing: Job.PreProcessing, PostProcessing: Job.PostProcessing, ErrorProcessing: Job.ErrorProcessing);
|
|
indexServer++;
|
|
if (indexServer >= lstServer.Count)
|
|
indexServer = 0;
|
|
QueryCount++;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return QueryCount;
|
|
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
return 0;
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
}
|
|
|
|
public int SetNxqlJobsColumns()
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
try
|
|
{
|
|
var QueryCount = 0;
|
|
foreach (var PortalResult in ValidationResults.Values)
|
|
{
|
|
var Portal = PortalResult.Portal;
|
|
var lstServer = Portal.Engines.Values.ToList();
|
|
if (lstServer.Count == 0)
|
|
continue;
|
|
|
|
var indexServer = 0;
|
|
foreach (var SubTableResult in PortalResult.TableResults.Values)
|
|
{
|
|
if (!SubTableResult.IsValid)
|
|
continue;
|
|
|
|
var SubTable = SubTableResult.Table;
|
|
if (!(SubTable.Template is cDataHistoryConfigQueryTemplateNxql NxqlTemplate))
|
|
continue;
|
|
|
|
var tableInfo = new cNxqlColumnValidatorColumnTableInfo();
|
|
foreach (var Column in SubTable.Columns.Values)
|
|
{
|
|
if (!cDataHistoryCollectorNxql.IsNxqlColumn(Column))
|
|
continue;
|
|
|
|
if (Column is cDataHistoryConfigColumn _C && _C.SourceType == eDataHistoryQueryType.Static)
|
|
continue;
|
|
|
|
var Server = lstServer[indexServer];
|
|
|
|
var strNxqlQuery = cDataHistoryCollectorNxql.getNxqlStatement(NxqlTemplate.Query, SubTable.ParentTable.ParentCluster.InformationClass, null, SubTable.ParentTable.KeyColumns, 0, null, null, new List<cDataHistoryConfigColumnBase>() { Column }, SubTable.Name);
|
|
if (string.IsNullOrEmpty(strNxqlQuery))
|
|
AddResultColumn(Portal, Column, int.MaxValue, false, "", "Could not get valid NXQL query for the table definition.");
|
|
else
|
|
{
|
|
var Job = new cNxqlColumnValidatorColumnJob(this, tableInfo, Column, Portal, strNxqlQuery);
|
|
Orchestrator.AddJob($"{Portal.Name}|{SubTable.Name}|{Column.Name}", Server.Address, strNxqlQuery, Portal.Credential, Port: Server.Port.ToString(), PostProcessing: Job.PostProcessing, ErrorProcessing: Job.ErrorProcessing);
|
|
indexServer++;
|
|
if (indexServer >= lstServer.Count)
|
|
indexServer = 0;
|
|
QueryCount++;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
return QueryCount;
|
|
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
return 0;
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
}
|
|
|
|
public async Task DoValidationAsync(cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
|
|
{
|
|
if (!(Collector?.InfrastructureConfig?.Nexthink?.Portals?.Count > 0))
|
|
return;
|
|
|
|
var QueryCount = SetNxqlJobsTables();
|
|
if (QueryCount > 0)
|
|
await Orchestrator.ProcessAsync(Token);
|
|
|
|
QueryCount = SetNxqlJobsColumns();
|
|
if (QueryCount > 0)
|
|
await Orchestrator.ProcessAsync(Token);
|
|
|
|
await ValidationResults.UpdateToSqlAsync(Collector, requestInfo, LogDeep + 1, Token);
|
|
}
|
|
|
|
public void AddResultTable(cDataHistoryNxtPortal Portal, cDataHistoryConfigSubTable Table, bool IsValid, string NXQL, string Message)
|
|
{
|
|
lock (ValidationResults)
|
|
{
|
|
if (!ValidationResults.TryGetValue(Portal.Name, out var ResultPortal))
|
|
{
|
|
ResultPortal = new cNxqlPortalValidationResult() { Portal = Portal };
|
|
ValidationResults.Add(Portal.Name, ResultPortal);
|
|
}
|
|
|
|
if (!ResultPortal.TableResults.TryGetValue(Table.Name, out var TableResult))
|
|
{
|
|
TableResult = new cNxqlTableValidationResult() { Table = Table, IsValid = IsValid, ErrorMessage = Message, NXQL = NXQL };
|
|
ResultPortal.TableResults.Add(Table.Name, TableResult);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void AddResultColumn(cDataHistoryNxtPortal Portal, cDataHistoryConfigColumnBase Column, int columnCount, bool IsValid, string NXQL, string Message)
|
|
{
|
|
lock (ValidationResults)
|
|
{
|
|
if (!ValidationResults.TryGetValue(Portal.Name, out var PortalResults))
|
|
return;
|
|
|
|
if (!PortalResults.TableResults.TryGetValue(Column.ParentTable?.Name, out var TableResult))
|
|
return;
|
|
|
|
TableResult.ColumnResults[Column.Name] = new cNxqlColumnValidationResult() { Column = Column, IsValid = IsValid, ErrorMessage = Message, NXQL = NXQL };
|
|
|
|
if (!IsValid)
|
|
this.Collector?.DoProcessUiMessage(0, $"Error : column validation of '{Column.ParentTable.Name}/{Column.Name}' on portal '{Portal.Name}' failed.");
|
|
|
|
if (TableResult.ColumnResults.Count == columnCount)
|
|
this.Collector?.DoProcessUiMessage(0, $"Column validation of NXQL table '{Column.ParentTable.Name}' on portal '{Portal.Name}' finished.");
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
public class cNxqlColumnValidatorTableJob
|
|
{
|
|
public readonly cNxqlColumnValidator Validator;
|
|
public readonly cDataHistoryConfigSubTable Table;
|
|
public readonly cDataHistoryNxtPortal Portal;
|
|
public readonly string NXQL;
|
|
|
|
public cNxqlColumnValidatorTableJob(cNxqlColumnValidator Validator, cDataHistoryConfigSubTable Table, cDataHistoryNxtPortal Portal, string NXQL)
|
|
{
|
|
this.Validator = Validator;
|
|
this.Table = Table;
|
|
this.Portal = Portal;
|
|
this.NXQL = NXQL;
|
|
}
|
|
|
|
public async Task PreProcessing(string Content, CancellationToken Token)
|
|
{
|
|
//Validator.Collector?.DoProcessUiMessage(0, $"Start validation NXQL table '{Table.Name}' on portal '{Portal.Name}'.");
|
|
await Task.CompletedTask;
|
|
}
|
|
|
|
public async Task PostProcessing(string Content, CancellationToken Token)
|
|
{
|
|
var Valid = !string.IsNullOrEmpty(Content);
|
|
Validator.AddResultTable(Portal, Table, Valid, NXQL, Valid ? null : "empty result returned.");
|
|
if (Valid)
|
|
Validator.Collector?.DoProcessUiMessage(0, $"Base validation of NXQL table '{Table.Name}' on portal '{Portal.Name}' succesfull.");
|
|
else
|
|
Validator.Collector?.DoProcessUiMessage(0, $"Error: Base validation of NXQL table '{Table.Name}' on portal '{Portal.Name}' failed.");
|
|
|
|
await Task.CompletedTask;
|
|
}
|
|
|
|
public async Task ErrorProcessing(string Content, CancellationToken Token)
|
|
{
|
|
Validator.AddResultTable(Portal, Table, false, NXQL, Content);
|
|
Validator.Collector?.DoProcessUiMessage(0, $"Error: Base validation of NXQL table '{Table.Name}' on portal '{Portal.Name}' failed.");
|
|
await Task.CompletedTask;
|
|
}
|
|
}
|
|
|
|
public class cNxqlColumnValidatorColumnTableInfo
|
|
{
|
|
public int ColCount = 0;
|
|
}
|
|
|
|
public class cNxqlColumnValidatorColumnJob
|
|
{
|
|
public readonly cNxqlColumnValidator Validator;
|
|
public readonly cDataHistoryConfigColumnBase TableColumn;
|
|
public readonly cDataHistoryNxtPortal Portal;
|
|
public readonly cNxqlColumnValidatorColumnTableInfo TableInfo;
|
|
public readonly string NXQL;
|
|
|
|
public cNxqlColumnValidatorColumnJob(cNxqlColumnValidator Validator, cNxqlColumnValidatorColumnTableInfo TableInfo, cDataHistoryConfigColumnBase TableColumn, cDataHistoryNxtPortal Portal, string NXQL)
|
|
{
|
|
this.Validator = Validator;
|
|
this.TableInfo = TableInfo;
|
|
TableInfo.ColCount++;
|
|
this.TableColumn = TableColumn;
|
|
this.Portal = Portal;
|
|
this.NXQL = NXQL;
|
|
}
|
|
|
|
public async Task PostProcessing(string Content, CancellationToken Token)
|
|
{
|
|
var Valid = !string.IsNullOrEmpty(Content);
|
|
Validator.AddResultColumn(Portal, TableColumn, TableInfo.ColCount, Valid, NXQL, Valid ? null : "empty result returned.");
|
|
await Task.CompletedTask;
|
|
}
|
|
|
|
public async Task ErrorProcessing(string Content, CancellationToken Token)
|
|
{
|
|
Validator.AddResultColumn(Portal, TableColumn, int.MaxValue, false, NXQL, Content);
|
|
await Task.CompletedTask;
|
|
}
|
|
|
|
}
|
|
|
|
public class cNxqlColumnsValidationResult : Dictionary<string, cNxqlPortalValidationResult>
|
|
{
|
|
public async Task<bool> LoadFromSqlAsync(cDataHistoryCollector Collector, CancellationToken Token, cF4sdWebRequestInfo requestInfo, int LogDeep)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
|
|
var _startTime = DateTime.UtcNow;
|
|
|
|
try
|
|
{
|
|
if (!DataHistorySqlHelper.GetWellKnownSqlStatement("LoadColumnsValidationResult", out var Query))
|
|
return false;
|
|
|
|
this.Clear();
|
|
|
|
foreach (var Portal in Collector.InfrastructureConfig.Nexthink.Portals.Values)
|
|
{
|
|
var Instance = Portal.Name;
|
|
var InstanceResult = new cNxqlPortalValidationResult();
|
|
this[Instance] = InstanceResult;
|
|
|
|
foreach (var Table in Collector.ClusterConfig.Tables.Values)
|
|
{
|
|
if (Table.ParentCluster.Origin != enumDataHistoryOrigin.NexthinkNxql)
|
|
continue;
|
|
|
|
foreach (var SubTable in Table.SubTables.Values)
|
|
{
|
|
var SubTableName = SubTable.Name;
|
|
var SubTableResult = new cNxqlTableValidationResult() { Table = SubTable };
|
|
|
|
InstanceResult.TableResults[SubTableName] = SubTableResult;
|
|
|
|
foreach (var Column in SubTable.Columns.Values)
|
|
{
|
|
if (Column is cDataHistoryConfigColumnComputation)
|
|
continue;
|
|
|
|
var ColumnResult = new cNxqlColumnValidationResult() { Column = Column };
|
|
SubTableResult.ColumnResults[Column.Name] = ColumnResult;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var Params = new Dictionary<string, object>
|
|
{
|
|
{ "Connector", "NXQL" }
|
|
};
|
|
|
|
var sqlerror = 0;
|
|
var sqlStartTime = DateTime.UtcNow;
|
|
|
|
try
|
|
{
|
|
using (var Conn = new cDbConnection(Collector.mainDbConnection))
|
|
{
|
|
if (!Conn.IsOpen)
|
|
{
|
|
LogEntry($"Could not open main sql database '{Collector.mainDbConnection.Database}', aborting saving NXQL column validation result", LogLevels.Error);
|
|
return false;
|
|
}
|
|
|
|
using (var Reader = await DataHistorySqlHelper.GetTableResultAsync(Conn, Query, Params, CancellationToken.None))
|
|
{
|
|
if (Reader == null)
|
|
return false;
|
|
|
|
try
|
|
{
|
|
if (Reader.HasRows)
|
|
{
|
|
while (await Reader.ReadAsync())
|
|
{
|
|
try
|
|
{
|
|
var Instance = Reader.GetString(0);
|
|
var SubTableName = Reader.GetString(1);
|
|
var ColumnName = Reader.GetString(2);
|
|
var IsValid = Reader.GetByte(3);
|
|
|
|
if (this.TryGetValue(Instance, out var InstanceResult))
|
|
if (InstanceResult.TableResults.TryGetValue(SubTableName, out var TableResult))
|
|
if (TableResult.ColumnResults.TryGetValue(ColumnName, out var ColumnResult))
|
|
ColumnResult.IsValid = IsValid == 1;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
cLogManager.DefaultLogger.LogException(E);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
cLogManager.DefaultLogger.LogException(E);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
sqlerror = E.HResult;
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (DataHistorySqlHelper.LogSql) DataHistorySqlHelper.SaveSqlTimingEntry(Query.Name, sqlStartTime, Params, sqlerror, requestInfo?.requestName);
|
|
}
|
|
|
|
foreach (var InstanceResult in this.Values)
|
|
{
|
|
foreach (var TableResult in InstanceResult.TableResults.Values)
|
|
{
|
|
TableResult.IsValid = false;
|
|
foreach (var ColumnResult in TableResult.ColumnResults.Values)
|
|
TableResult.IsValid |= ColumnResult.IsValid;
|
|
if (!TableResult.IsValid)
|
|
TableResult.ColumnResults.Clear();
|
|
}
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public async Task UpdateToSqlAsync(cDataHistoryCollector Collector, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
try
|
|
{
|
|
using (var Conn = new cDbConnection(Collector.mainDbConnection))
|
|
{
|
|
if (!Conn.IsOpen)
|
|
{
|
|
LogEntry($"Could not open main sql database '{Collector.mainDbConnection.Database}', aborting saving NXQL column validation result", LogLevels.Error);
|
|
return;
|
|
}
|
|
|
|
foreach (var PortalEntry in this.Values)
|
|
{
|
|
foreach (var SubtableEntry in PortalEntry.TableResults.Values)
|
|
{
|
|
if (!SubtableEntry.IsValid)
|
|
{
|
|
foreach (var ColumnEntry in SubtableEntry.Table.Columns.Values)
|
|
await SaveEntryAsync(Conn, PortalEntry.Portal.Name, ColumnEntry, false, requestInfo, LogDeep + 1, Token);
|
|
|
|
}
|
|
else
|
|
{
|
|
foreach (var ColumnEntry in SubtableEntry.ColumnResults.Values)
|
|
await SaveEntryAsync(Conn, PortalEntry.Portal.Name, ColumnEntry.Column, ColumnEntry.IsValid, requestInfo, LogDeep + 1, Token);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
}
|
|
|
|
private async Task SaveEntryAsync(cDbConnection Conn, string Portal, cDataHistoryConfigColumnBase Column, bool IsValid, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
|
|
{
|
|
try
|
|
{
|
|
var Params = new Dictionary<string, object>() { { "Connector", "NXQL" }, { "Instance", Portal }, { "SubTable", Column.ParentTable.Name }, { "Column", Column.Name }, { "isValid", IsValid } };
|
|
await DataHistorySqlHelper.DoUpdateOrInsertAsync(Conn, "main-column-validation", Params, new List<string>() { "Connector", "Instance", "SubTable", "Column" }, requestInfo, LogDeep + 1, Token);
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class cNxqlPortalValidationResult
|
|
{
|
|
public cDataHistoryNxtPortal Portal;
|
|
public Dictionary<string, cNxqlTableValidationResult> TableResults = new Dictionary<string, cNxqlTableValidationResult>();
|
|
}
|
|
|
|
public class cNxqlTableValidationResult
|
|
{
|
|
public cDataHistoryConfigSubTable Table;
|
|
public bool IsValid;
|
|
public string ErrorMessage;
|
|
public string NXQL;
|
|
public Dictionary<string, cNxqlColumnValidationResult> ColumnResults = new Dictionary<string, cNxqlColumnValidationResult>();
|
|
}
|
|
|
|
public class cNxqlColumnValidationResult
|
|
{
|
|
public cDataHistoryConfigColumnBase Column;
|
|
public bool IsValid;
|
|
public string NXQL;
|
|
public string ErrorMessage;
|
|
}
|
|
|
|
public class cNxqlDailyCollector
|
|
{
|
|
public class cTableResult : Dictionary<string, object>
|
|
{
|
|
public enumFasdInformationClass InformationClass = enumFasdInformationClass.Unknown;
|
|
}
|
|
|
|
public readonly cDataHistoryCollector Collector;
|
|
public readonly cNxqlOrchestration Orchestrator;
|
|
public readonly cNxqlColumnsValidationResult ColumnsValidationResults;
|
|
|
|
public Dictionary<string, cTableResult> Results = new Dictionary<string, cTableResult>();
|
|
|
|
public cNxqlDailyCollector(cDataHistoryCollector Collector, cNxqlColumnsValidationResult ColumnsValidationResults)
|
|
{
|
|
this.Collector = Collector;
|
|
this.ColumnsValidationResults = ColumnsValidationResults;
|
|
Orchestrator = new cNxqlOrchestration("DailyCollector");
|
|
}
|
|
|
|
public void CreateJobs(List<cDataHistoryConfigTable> Tables, DateTime From, DateTime To, string strEngine, Int64 Nxt_Id, enumFasdInformationClass InfoClass)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
try
|
|
{
|
|
// get the engine, portal & column validation result for the engine
|
|
cDataHistoryNxtEngine EngineComputer = null;
|
|
cDataHistoryNxtPortal PortalComputer = null;
|
|
cNxqlPortalValidationResult PortalValidationResult = null; ;
|
|
|
|
foreach (var PortalEntry in Collector.InfrastructureConfig.Nexthink.Portals.Values)
|
|
{
|
|
foreach (var EngineEntry in PortalEntry.Engines.Values)
|
|
{
|
|
if (EngineEntry.Name == strEngine)
|
|
{
|
|
EngineComputer = EngineEntry;
|
|
PortalComputer = PortalEntry;
|
|
if (!ColumnsValidationResults.TryGetValue(PortalComputer.Name, out PortalValidationResult))
|
|
{
|
|
LogEntry($"no valid colum validation result for portal '{PortalComputer.Name}' available.", LogLevels.Error);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (EngineComputer == null)
|
|
{
|
|
LogEntry($"no valid engine configuration entry found for engine name '{strEngine}' available.", LogLevels.Error);
|
|
return;
|
|
}
|
|
|
|
if (PortalValidationResult == null)
|
|
{
|
|
LogEntry($"no valid colum validation result for portal '{PortalComputer.Name}' available.", LogLevels.Error);
|
|
return;
|
|
}
|
|
|
|
foreach (var Table in Tables)
|
|
{
|
|
if (!(Table.ParentCluster.Origin == enumDataHistoryOrigin.NexthinkNxql))
|
|
continue;
|
|
|
|
if (Table.ParentCluster.InformationClass != InfoClass)
|
|
continue;
|
|
|
|
var TableCollector = new cNxqlDailyCollectorTable(this, EngineComputer, Table);
|
|
|
|
foreach (var SubTable in Table.SubTables.Values)
|
|
{
|
|
if (!PortalValidationResult.TableResults.TryGetValue(SubTable.Name, out var SubTableValidation))
|
|
continue;
|
|
|
|
if (!(SubTable.Template is cDataHistoryConfigQueryTemplateNxql NxqlTemplate))
|
|
continue;
|
|
|
|
var Cols = new List<cDataHistoryConfigColumnBase>();
|
|
foreach (var _res in SubTableValidation.ColumnResults.Values)
|
|
if (_res.IsValid)
|
|
Cols.Add(_res.Column);
|
|
if (!SubTable.Columns.TryGetValue("id_int", out var _keyCol))
|
|
{
|
|
LogEntry($"no valid column 'id_int' found in subtable configuration '{SubTable.Name}'", LogLevels.Warning);
|
|
return;
|
|
}
|
|
|
|
var strNxqlQuery = cDataHistoryCollectorNxql.getNxqlStatement(NxqlTemplate.Query, Table.ParentCluster.InformationClass, new List<object>(1) { Nxt_Id }, new List<cDataHistoryConfigColumnBase>(1) { _keyCol }, NxqlTemplate.Limit, From, To, Cols, SubTable.Name);
|
|
|
|
var SubTableCollector = new cNxqlDailyCollectorSubTable(strNxqlQuery, SubTable, TableCollector, EngineComputer);
|
|
|
|
Orchestrator.AddJob($"{EngineComputer.Name}|{SubTable.Name}", EngineComputer.Address, strNxqlQuery, PortalComputer.Credential, Port: EngineComputer.Port.ToString(), PostProcessing: SubTableCollector.PostProcessing, ErrorProcessing: SubTableCollector.ErrorProcessing);
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
public class cNxqlDailyCollectorTable
|
|
{
|
|
public readonly cNxqlDailyCollector DataCollector;
|
|
public readonly cDataHistoryConfigTable Table;
|
|
public readonly cDataHistoryNxtEngine Engine;
|
|
|
|
private readonly Dictionary<string, cNxqlDailyCollectorSubTable> SubTableCollectors = new Dictionary<string, cNxqlDailyCollectorSubTable>();
|
|
|
|
public cNxqlDailyCollectorTable(cNxqlDailyCollector DataCollector, cDataHistoryNxtEngine Engine, cDataHistoryConfigTable Table)
|
|
{
|
|
this.DataCollector = DataCollector;
|
|
this.Engine = Engine;
|
|
this.Table = Table;
|
|
}
|
|
|
|
public void RegisterSubTableCollector(cNxqlDailyCollectorSubTable Collector)
|
|
{
|
|
SubTableCollectors[Collector.SubTable.Name] = Collector;
|
|
}
|
|
|
|
public async Task ProcessResult(string SubTable, CancellationToken Token)
|
|
{
|
|
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
try
|
|
{
|
|
if (!SubTableCollectors.TryGetValue(SubTable, out var Collector))
|
|
return;
|
|
|
|
var isFinished = true;
|
|
lock (SubTableCollectors)
|
|
{
|
|
Collector.finished = true;
|
|
foreach (var SubCollector in SubTableCollectors.Values)
|
|
isFinished &= SubCollector.finished;
|
|
}
|
|
if (!isFinished)
|
|
return;
|
|
|
|
var Cols = new cTableResult();
|
|
|
|
foreach (var SubTableResult in SubTableCollectors.Values)
|
|
{
|
|
if (SubTableResult.Result == null)
|
|
continue;
|
|
|
|
// get the infos, which csv column refers to which subtable column
|
|
var ColInfos = new Dictionary<string, cNxqlColumnInfo>();
|
|
for (int i = 0; i < SubTableResult.Result.Columns.Length; i++)
|
|
{
|
|
var colNameCsv = SubTableResult.Result.Columns[i].ToLowerInvariant();
|
|
bool found = false;
|
|
foreach (var SubTableColumnEntry in SubTableResult.SubTable.Columns.Values)
|
|
{
|
|
if (SubTableColumnEntry is cDataHistoryConfigColumnComputation)
|
|
continue;
|
|
var colNameST = cDataHistoryCollectorNxql.GetNxqlColumnResultName(SubTableColumnEntry);
|
|
if (colNameST == colNameCsv)
|
|
{
|
|
var colInfo = new cNxqlColumnInfo()
|
|
{
|
|
Column = SubTableColumnEntry,
|
|
ColIndex = i
|
|
};
|
|
ColInfos[SubTableColumnEntry.Name] = colInfo;
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
LogEntry($"No matching column could be found for NXQL column '{colNameCsv}' in sub-table '{SubTableResult.SubTable.Name}'", LogLevels.Warning);
|
|
|
|
// ad the static value to the column list
|
|
foreach (var SubTableColumnEntry in SubTableResult.SubTable.Columns.Values)
|
|
{
|
|
if (SubTableColumnEntry is cDataHistoryConfigColumn col)
|
|
{
|
|
if (col.SourceType == eDataHistoryQueryType.Static && col.SourceName.ToLowerInvariant() == "engine")
|
|
{
|
|
var colInfo = new cNxqlColumnInfo()
|
|
{
|
|
Column = SubTableColumnEntry,
|
|
ColIndex = -1,
|
|
StaticValue = Engine.Name
|
|
};
|
|
ColInfos[SubTableColumnEntry.Name] = colInfo;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SubTableResult.SubTable.Aggregation)
|
|
{
|
|
var Aggs = new Dictionary<string, cNxqlAggregationEntry>(ColInfos.Count);
|
|
|
|
foreach (var _colInfo in ColInfos.Values)
|
|
{
|
|
if (!(_colInfo.Column is cDataHistoryConfigColumn _col))
|
|
continue;
|
|
|
|
Aggs.Add(_colInfo.Column.Name, new cNxqlAggregationEntry(_col));
|
|
}
|
|
|
|
foreach (var DataRecord in SubTableResult.Result.Data)
|
|
{
|
|
foreach (var _colInfo in ColInfos.Values)
|
|
{
|
|
if (!Aggs.TryGetValue(_colInfo.Column.Name, out var agg))
|
|
continue;
|
|
|
|
if (_colInfo.ColIndex < 0)
|
|
agg.addValue(_colInfo.StaticValue, null);
|
|
else
|
|
agg.addValue(DataRecord[_colInfo.ColIndex], null);
|
|
}
|
|
}
|
|
|
|
foreach (var _colInfo in ColInfos.Values)
|
|
{
|
|
if (!Aggs.TryGetValue(_colInfo.Column.Name, out var agg))
|
|
continue;
|
|
if (!(_colInfo.Column is cDataHistoryConfigColumn _col))
|
|
continue;
|
|
|
|
var _aggVal = agg.getValue();
|
|
var dtype = cDataHistoryConfigClusters.ResultingAggregationType(_col.ValueType, _col.AggregationType);
|
|
var sqlVals = DataHistorySqlHelper.GetSqlDataValueFromHistoryType(_aggVal, dtype, _col.Cardinal);
|
|
if (sqlVals != null)
|
|
{
|
|
if (sqlVals.Length > 0)
|
|
{
|
|
Cols[_colInfo.Column.Name] = sqlVals[0];
|
|
/*
|
|
if (sqlVals.Length > 1)
|
|
Cols[_colInfo.Column.Name + "_bin"] = sqlVals[1];
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (SubTableResult.Result.Data.Count > 0)
|
|
{
|
|
var DataRecord = SubTableResult.Result.Data[0];
|
|
foreach (var _colInfo in ColInfos.Values)
|
|
{
|
|
string val = null;
|
|
if (_colInfo.ColIndex < 0)
|
|
val = _colInfo.StaticValue;
|
|
else
|
|
val = DataRecord[_colInfo.ColIndex];
|
|
|
|
var sqlVals = DataHistorySqlHelper.GetSqlDataValueFromHistoryType(val, _colInfo.Column.ValueType, _colInfo.Column.Cardinal);
|
|
if (sqlVals != null)
|
|
{
|
|
if (sqlVals.Length > 0)
|
|
{
|
|
Cols[_colInfo.Column.Name] = sqlVals[0];
|
|
if (sqlVals.Length > 1)
|
|
Cols[_colInfo.Column.Name + "_bin"] = sqlVals[1];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
lock (this.DataCollector.Results)
|
|
this.DataCollector.Results[this.Table.Name] = Cols;
|
|
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
await Task.CompletedTask;
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class cNxqlDailyCollectorSubTable
|
|
{
|
|
public readonly cDataHistoryConfigSubTable SubTable;
|
|
public readonly cNxqlDailyCollectorTable TableCollector;
|
|
public readonly cDataHistoryNxtEngine Server;
|
|
public readonly string NXQL;
|
|
|
|
public cCsvFile Result = null;
|
|
public bool finished = false;
|
|
|
|
public cNxqlDailyCollectorSubTable(string NXQL, cDataHistoryConfigSubTable SubTable, cNxqlDailyCollectorTable TableCollector, cDataHistoryNxtEngine Server)
|
|
{
|
|
this.NXQL = NXQL;
|
|
this.SubTable = SubTable;
|
|
this.TableCollector = TableCollector;
|
|
this.Server = Server;
|
|
TableCollector.RegisterSubTableCollector(this);
|
|
}
|
|
|
|
private void LogContent(string Content)
|
|
{
|
|
var Lines = Content.Split('\n');
|
|
var arrDebug = new List<string>(5)
|
|
{
|
|
$"NXQL query for sub-table '{SubTable.Name}': {NXQL}",
|
|
"Resulting lines: " + Lines.Length.ToString()
|
|
};
|
|
string str = "Line1: ";
|
|
if (Lines.Length >= 1)
|
|
str += Lines[0].Substring(0, Math.Min(255, Lines[0].Length));
|
|
arrDebug.Add(str);
|
|
str = "Line2: ";
|
|
if (Lines.Length >= 2)
|
|
str += Lines[1].Substring(0, Math.Min(255, Lines[1].Length));
|
|
arrDebug.Add(str);
|
|
str = "Line3: ";
|
|
if (Lines.Length >= 3)
|
|
str += Lines[2].Substring(0, Math.Min(255, Lines[2].Length));
|
|
arrDebug.Add(str);
|
|
cLogManager.DefaultLogger.LogList(LogLevels.Debug, arrDebug);
|
|
}
|
|
|
|
public async Task PostProcessing(string Content, CancellationToken Token)
|
|
{
|
|
var csvResult = new cCsvFile();
|
|
if (cLogManager.DefaultLogger.IsDebug)
|
|
LogContent(Content);
|
|
|
|
if (csvResult.ImportFromString(Content))
|
|
{
|
|
Result = csvResult;
|
|
|
|
var lines = Result.Data.Count;
|
|
TableCollector?.DataCollector?.Collector?.DoProcessUiMessage(0, $"NXQL query for sub-table {SubTable.Name} on server {Server.Name}({Server.Address}) returned {lines} lines", LogLevels.Debug);
|
|
|
|
await TableCollector.ProcessResult(SubTable.Name, Token);
|
|
}
|
|
else
|
|
{
|
|
LogEntry($"Could not convert result of NXQL query for subtable {SubTable.Name} on engine {Server.Name}({Server.Address}) to csv.", LogLevels.Error);
|
|
}
|
|
|
|
}
|
|
|
|
public async Task ErrorProcessing(string Content, CancellationToken Token)
|
|
{
|
|
await Task.CompletedTask;
|
|
LogEntry($"Could not get result of NXQL query for subtable {SubTable.Name} on engine {Server.Name}({Server.Address})", LogLevels.Error);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
public class cNxqlDataCollector
|
|
{
|
|
public readonly cDataHistoryCollector Collector;
|
|
public readonly cNxqlOrchestration Orchestrator;
|
|
public readonly Guid ScanId;
|
|
|
|
public readonly string FileSavePath = null;
|
|
|
|
public readonly cNxqlColumnsValidationResult ColumnsValidationResults;
|
|
|
|
public int validResultCount { get; internal set; } = 0;
|
|
public int errorResultCount { get; internal set; } = 0;
|
|
|
|
public cNxqlDataCollector(cDataHistoryCollector Collector, cNxqlColumnsValidationResult ColumnsValidationResults, Guid ScanId, bool SaveResultsToFile)
|
|
{
|
|
this.Collector = Collector;
|
|
this.ColumnsValidationResults = ColumnsValidationResults;
|
|
this.ScanId = ScanId;
|
|
if (SaveResultsToFile)
|
|
{
|
|
if (cLogManager.Instance is cLogManagerFile log)
|
|
{
|
|
try
|
|
{
|
|
var fn = log.GetLogFileName();
|
|
fn = Path.Combine(fn, "..\\NxqlQueryResults");
|
|
if (Directory.Exists(fn))
|
|
{
|
|
var di = new DirectoryInfo(fn);
|
|
foreach (FileInfo file in di.EnumerateFiles())
|
|
file.Delete();
|
|
foreach (DirectoryInfo dir in di.EnumerateDirectories())
|
|
dir.Delete(true);
|
|
}
|
|
Directory.CreateDirectory(fn);
|
|
if (Directory.Exists(fn))
|
|
FileSavePath = fn;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
}
|
|
}
|
|
Orchestrator = new cNxqlOrchestration("DataCollector");
|
|
}
|
|
|
|
public void CreateJobs(DateTime From, DateTime To, cF4sdWebRequestInfo requestInfo, int LogDeep)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
|
|
var _startTime = DateTime.UtcNow;
|
|
|
|
try
|
|
{
|
|
foreach (var Portal in Collector.InfrastructureConfig?.Nexthink?.Portals?.Values)
|
|
{
|
|
if (!ColumnsValidationResults.TryGetValue(Portal.Name, out var PortalValidationResult))
|
|
continue;
|
|
|
|
foreach (var Engine in Portal.Engines.Values)
|
|
{
|
|
foreach (var Table in Collector.ClusterConfig.Tables.Values)
|
|
{
|
|
if (!(Table.ParentCluster.Origin == enumDataHistoryOrigin.NexthinkNxql))
|
|
continue;
|
|
|
|
if (Table.Cached == eDataHistoryTableCached.No && FileSavePath == null)
|
|
continue;
|
|
|
|
var TableCollector = new cNxqlDataCollectorTable(this, Engine, Table);
|
|
|
|
foreach (var SubTable in Table.SubTables.Values)
|
|
{
|
|
if (!PortalValidationResult.TableResults.TryGetValue(SubTable.Name, out var SubTableValidation))
|
|
continue;
|
|
|
|
if (!(SubTable.Template is cDataHistoryConfigQueryTemplateNxql NxqlTemplate))
|
|
continue;
|
|
|
|
var Cols = new List<cDataHistoryConfigColumnBase>();
|
|
foreach (var _res in SubTableValidation.ColumnResults.Values)
|
|
if (_res.IsValid)
|
|
Cols.Add(_res.Column);
|
|
|
|
var strNxqlQuery = cDataHistoryCollectorNxql.getNxqlStatement(NxqlTemplate.Query, Table.ParentCluster.InformationClass, null, null, NxqlTemplate.Limit, From, To, Cols, SubTable.Name);
|
|
|
|
var SubTableCollector = new cNxqlDataCollectorSubTable(strNxqlQuery, SubTable, TableCollector, Engine, requestInfo, LogDeep + 1);
|
|
|
|
Orchestrator.AddJob($"{Engine.Name}|{SubTable.Name}", Engine.Address, strNxqlQuery, Portal.Credential, Port: Engine.Port.ToString(), PostProcessing: SubTableCollector.PostProcessing, ErrorProcessing: SubTableCollector.ErrorProcessing);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class cNxqlDataCollectorTable
|
|
{
|
|
public readonly cNxqlDataCollector DataCollector;
|
|
public readonly cDataHistoryConfigTable Table;
|
|
public readonly cDataHistoryNxtEngine Engine;
|
|
|
|
private readonly Dictionary<string, cNxqlDataCollectorSubTable> SubTableCollectors = new Dictionary<string, cNxqlDataCollectorSubTable>();
|
|
|
|
public cNxqlDataCollectorTable(cNxqlDataCollector DataCollector, cDataHistoryNxtEngine Engine, cDataHistoryConfigTable Table)
|
|
{
|
|
this.DataCollector = DataCollector;
|
|
this.Engine = Engine;
|
|
this.Table = Table;
|
|
}
|
|
|
|
public void RegisterSubTableCollector(cNxqlDataCollectorSubTable Collector)
|
|
{
|
|
SubTableCollectors[Collector.SubTable.Name] = Collector;
|
|
}
|
|
|
|
public async Task ProcessResult(string SubTable, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token)
|
|
{
|
|
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
|
|
var _startTime = DateTime.UtcNow;
|
|
|
|
try
|
|
{
|
|
if (!SubTableCollectors.TryGetValue(SubTable, out var Collector))
|
|
return;
|
|
|
|
var isFinished = true;
|
|
lock (SubTableCollectors)
|
|
{
|
|
Collector.finished = true;
|
|
foreach (var SubCollector in SubTableCollectors.Values)
|
|
isFinished &= SubCollector.finished;
|
|
}
|
|
if (!isFinished)
|
|
return;
|
|
|
|
|
|
using (var Conn = new cDbConnection(DataCollector.Collector.mainDbConnection))
|
|
{
|
|
var TableName = SubTableCollectors?.Values?.First()?.SubTable?.ParentTable.SourceName;
|
|
if (!Conn.IsOpen)
|
|
{
|
|
LogEntry($"Could not open main sql database '{DataCollector.Collector.mainDbConnection.Database}', aborting saving result for nxql table '{TableName}'", LogLevels.Error);
|
|
DataCollector.errorResultCount++;
|
|
return;
|
|
}
|
|
|
|
var firstSubTable = true;
|
|
|
|
foreach (var SubTableResult in SubTableCollectors.Values)
|
|
{
|
|
if (SubTableResult.Result == null)
|
|
continue;
|
|
|
|
if (SubTableResult.SubTable.ParentTable.Cached != eDataHistoryTableCached.Yes)
|
|
continue;
|
|
|
|
// get the infos, which csv column refers to which subtable column
|
|
var ColInfos = new Dictionary<string, cNxqlColumnInfo>();
|
|
for (int i = 0; i < SubTableResult.Result.Columns.Length; i++)
|
|
{
|
|
var colNameCsv = SubTableResult.Result.Columns[i].ToLowerInvariant();
|
|
bool found = false;
|
|
foreach (var SubTableColumnEntry in SubTableResult.SubTable.Columns.Values)
|
|
{
|
|
if (SubTableColumnEntry is cDataHistoryConfigColumnComputation)
|
|
continue;
|
|
var colNameST = cDataHistoryCollectorNxql.GetNxqlColumnResultName(SubTableColumnEntry);
|
|
if (colNameST == colNameCsv)
|
|
{
|
|
var colInfo = new cNxqlColumnInfo()
|
|
{
|
|
Column = SubTableColumnEntry,
|
|
ColIndex = i
|
|
};
|
|
ColInfos[SubTableColumnEntry.Name] = colInfo;
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
LogEntry($"No matching column could be found for NXQL column '{colNameCsv}' in sub-table '{SubTableResult.SubTable.Name}'", LogLevels.Warning);
|
|
}
|
|
|
|
// ad the static value to the column list
|
|
foreach (var SubTableColumnEntry in SubTableResult.SubTable.Columns.Values)
|
|
{
|
|
if (SubTableColumnEntry is cDataHistoryConfigColumn col)
|
|
{
|
|
if (col.SourceType == eDataHistoryQueryType.Static && col.SourceName.ToLowerInvariant() == "engine")
|
|
{
|
|
var colInfo = new cNxqlColumnInfo()
|
|
{
|
|
Column = SubTableColumnEntry,
|
|
ColIndex = -1,
|
|
StaticValue = Engine.Name
|
|
};
|
|
ColInfos[SubTableColumnEntry.Name] = colInfo;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// check if we have all necessary key columns
|
|
var keyValid = true;
|
|
var IDs = new List<string>(SubTableResult.SubTable.ParentTable.KeyColumns.Count);
|
|
var idInfos = new List<cNxqlColumnInfo>(SubTableResult.SubTable.ParentTable.KeyColumns.Count);
|
|
if (SubTableResult.SubTable.ParentTable.Type == eDataHistoryTableType.History)
|
|
IDs.Add("ScanId");
|
|
|
|
foreach (var Entry in SubTableResult.SubTable.ParentTable.KeyColumns)
|
|
{
|
|
if (!ColInfos.TryGetValue(Entry.Name, out var _ci))
|
|
{
|
|
keyValid = false;
|
|
LogEntry($"Could not store the results for sub-table '{SubTableResult.SubTable.Name}' because the key column '{Entry.Name} could not be found in the NXQL result on engine '{Engine.Name}({Engine.Address})'", LogLevels.Error);
|
|
break;
|
|
}
|
|
IDs.Add(Entry.NameBin);
|
|
idInfos.Add(_ci);
|
|
}
|
|
if (!keyValid)
|
|
continue;
|
|
|
|
var RecordCount = 0;
|
|
var ErrorCount = 0;
|
|
|
|
if (SubTableResult.SubTable.Aggregation)
|
|
{
|
|
var groupingDic = new Dictionary<string, List<string[]>>();
|
|
foreach (var DataRecord in SubTableResult.Result.Data)
|
|
{
|
|
var strKey = "";
|
|
foreach (var colInfo in idInfos)
|
|
{
|
|
if (strKey != null)
|
|
strKey += '|';
|
|
if (colInfo.ColIndex >= 0)
|
|
strKey += DataRecord[colInfo.ColIndex];
|
|
}
|
|
if (!groupingDic.TryGetValue(strKey, out var listVals))
|
|
{
|
|
listVals = new List<string[]>();
|
|
groupingDic.Add(strKey, listVals);
|
|
}
|
|
listVals.Add(DataRecord);
|
|
|
|
}
|
|
|
|
foreach (var groupEntry in groupingDic.Values)
|
|
{
|
|
var Cols = new Dictionary<string, object>(ColInfos.Count)
|
|
{
|
|
{ "ScanId", DataCollector.ScanId }
|
|
};
|
|
foreach (var _colInfo in ColInfos.Values)
|
|
{
|
|
if (!(_colInfo.Column is cDataHistoryConfigColumn _col))
|
|
continue;
|
|
|
|
var agg = new cNxqlAggregationEntry(_col);
|
|
|
|
foreach (var ds in groupEntry)
|
|
{
|
|
if (_colInfo.ColIndex < 0)
|
|
agg.addValue(_colInfo.StaticValue, null);
|
|
else
|
|
agg.addValue(ds[_colInfo.ColIndex], null);
|
|
}
|
|
|
|
|
|
var _aggVal = agg.getValue();
|
|
var dtype = cDataHistoryConfigClusters.ResultingAggregationType(_col.ValueType, _col.AggregationType);
|
|
|
|
var sqlVals = DataHistorySqlHelper.GetSqlDataValueFromHistoryType(_aggVal, dtype, _colInfo.Column.Cardinal);
|
|
|
|
if (sqlVals != null)
|
|
{
|
|
if (sqlVals.Length > 0)
|
|
{
|
|
Cols[_colInfo.Column.Name] = sqlVals[0];
|
|
if (sqlVals.Length > 1)
|
|
Cols[_colInfo.Column.Name + "_bin"] = sqlVals[1];
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// insert or update the data record
|
|
bool isValid = false;
|
|
if (firstSubTable && SubTableResult.SubTable.ParentTable.Type != eDataHistoryTableType.Static)
|
|
isValid = await DataHistorySqlHelper.DoInsertAsync(Conn, TableName, Cols, Token, requestInfo, LogDeep + 1, LogSilent: true);
|
|
else if (SubTableResult.SubTable.ParentTable.Type != eDataHistoryTableType.Events)
|
|
isValid = await DataHistorySqlHelper.DoUpdateOrInsertAsync(Conn, TableName, Cols, IDs, requestInfo, LogDeep + 1, Token, LogSilent: true);
|
|
|
|
if (isValid)
|
|
RecordCount++;
|
|
else
|
|
ErrorCount++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (var DataRecord in SubTableResult.Result.Data)
|
|
{
|
|
var Cols = new Dictionary<string, object>(ColInfos.Count)
|
|
{
|
|
{ "ScanId", DataCollector.ScanId }
|
|
};
|
|
foreach (var _colInfo in ColInfos.Values)
|
|
{
|
|
string val = null;
|
|
if (_colInfo.ColIndex < 0)
|
|
val = _colInfo.StaticValue;
|
|
else
|
|
val = DataRecord[_colInfo.ColIndex];
|
|
|
|
var sqlVals = DataHistorySqlHelper.GetSqlDataValueFromHistoryType(val, _colInfo.Column.ValueType, _colInfo.Column.Cardinal);
|
|
if (sqlVals != null)
|
|
{
|
|
if (sqlVals.Length > 0)
|
|
{
|
|
Cols[_colInfo.Column.Name] = sqlVals[0];
|
|
if (sqlVals.Length > 1)
|
|
Cols[_colInfo.Column.Name + "_bin"] = sqlVals[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
// insert or update the data record
|
|
bool isValid = false;
|
|
if (firstSubTable && SubTableResult.SubTable.ParentTable.Type != eDataHistoryTableType.Static)
|
|
isValid = await DataHistorySqlHelper.DoInsertAsync(Conn, TableName, Cols, Token, requestInfo, LogDeep + 1, LogSilent: true);
|
|
else if (SubTableResult.SubTable.ParentTable.Type != eDataHistoryTableType.Events)
|
|
isValid = await DataHistorySqlHelper.DoUpdateOrInsertAsync(Conn, TableName, Cols, IDs, requestInfo, LogDeep + 1, Token, LogSilent: true);
|
|
|
|
if (isValid)
|
|
RecordCount++;
|
|
else
|
|
ErrorCount++;
|
|
}
|
|
|
|
}
|
|
|
|
LogEntry($"{RecordCount}/{SubTableResult?.Result?.Data?.Count} records inserted or updated for sub-table {SubTableResult?.SubTable?.Name} in table {SubTableResult?.SubTable?.ParentTable?.Name}", LogLevels.Debug);
|
|
if (ErrorCount != 0)
|
|
LogEntry($"{ErrorCount}/{SubTableResult?.Result?.Data?.Count} errors on inserts or updates for sub-table {SubTableResult?.SubTable?.Name} in table {SubTableResult?.SubTable?.ParentTable?.Name}", LogLevels.Error);
|
|
|
|
var _sum = ErrorCount + RecordCount;
|
|
var isValidResult = false;
|
|
if (_sum > 0 && (ErrorCount * 1000 / _sum) >= 100)
|
|
DataCollector.errorResultCount++;
|
|
else
|
|
{
|
|
DataCollector.validResultCount++;
|
|
isValidResult = true;
|
|
}
|
|
|
|
var msg = $"Result for nxql-subtable '{SubTableResult?.SubTable?.Name}' on server '{Engine.Name}': {RecordCount} records inserted";
|
|
if (ErrorCount != 0)
|
|
msg += $", {ErrorCount} records failed";
|
|
msg += ".";
|
|
DataCollector?.Collector?.DoProcessUiMessage(0, msg);
|
|
if (isValidResult && ErrorCount != 0)
|
|
DataCollector?.Collector?.DoProcessUiMessage(1, $"The result is considered valid.");
|
|
else if (ErrorCount != 0)
|
|
DataCollector?.Collector?.DoProcessUiMessage(1, $"The result is considered invalid.");
|
|
|
|
firstSubTable = false;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
DataCollector.errorResultCount++;
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class cNxqlDataCollectorSubTable
|
|
{
|
|
public readonly cDataHistoryConfigSubTable SubTable;
|
|
public readonly cNxqlDataCollectorTable TableCollector;
|
|
public readonly cDataHistoryNxtEngine Server;
|
|
public readonly string NXQL;
|
|
|
|
public cCsvFile Result = null;
|
|
public bool finished = false;
|
|
|
|
private cF4sdWebRequestInfo requestInfo;
|
|
private int LogDeep;
|
|
|
|
public cNxqlDataCollectorSubTable(string NXQL, cDataHistoryConfigSubTable SubTable, cNxqlDataCollectorTable TableCollector, cDataHistoryNxtEngine Server, cF4sdWebRequestInfo requestInfo, int LogDeep)
|
|
{
|
|
this.NXQL = NXQL;
|
|
this.SubTable = SubTable;
|
|
this.TableCollector = TableCollector;
|
|
this.Server = Server;
|
|
this.requestInfo = requestInfo;
|
|
this.LogDeep = LogDeep;
|
|
TableCollector.RegisterSubTableCollector(this);
|
|
}
|
|
|
|
private void LogContent(string Content)
|
|
{
|
|
var Lines = Content.Split('\n');
|
|
var arrDebug = new List<string>(5)
|
|
{
|
|
$"NXQL query for sub-table '{SubTable.Name}': {NXQL}",
|
|
"Resulting lines: " + Lines.Length.ToString()
|
|
};
|
|
string str = "Line1: ";
|
|
if (Lines.Length >= 1)
|
|
str += Lines[0].Substring(0, Math.Min(255, Lines[0].Length));
|
|
arrDebug.Add(str);
|
|
str = "Line2: ";
|
|
if (Lines.Length >= 2)
|
|
str += Lines[1].Substring(0, Math.Min(255, Lines[1].Length));
|
|
arrDebug.Add(str);
|
|
str = "Line3: ";
|
|
if (Lines.Length >= 3)
|
|
str += Lines[2].Substring(0, Math.Min(255, Lines[2].Length));
|
|
arrDebug.Add(str);
|
|
cLogManager.DefaultLogger.LogList(LogLevels.Debug, arrDebug);
|
|
}
|
|
|
|
public async Task PostProcessing(string Content, CancellationToken Token)
|
|
{
|
|
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
|
|
var _startTime = DateTime.UtcNow;
|
|
|
|
try
|
|
{
|
|
if (TableCollector.DataCollector.FileSavePath != null)
|
|
{
|
|
try
|
|
{
|
|
var fn = Path.Combine(TableCollector.DataCollector.FileSavePath, $"NXQL-{SubTable.Name}-{Server.Name}({Server.Address}).txt");
|
|
File.WriteAllText(fn, Content, Encoding.UTF8);
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
}
|
|
var csvResult = new cCsvFile();
|
|
if (cLogManager.DefaultLogger.IsDebug)
|
|
LogContent(Content);
|
|
|
|
if (csvResult.ImportFromString(Content))
|
|
{
|
|
Result = csvResult;
|
|
|
|
var lines = Result.Data.Count;
|
|
TableCollector?.DataCollector?.Collector?.DoProcessUiMessage(0, $"NXQL query for sub-table {SubTable.Name} on server {Server.Name}({Server.Address}) returned {lines} lines");
|
|
|
|
await TableCollector.ProcessResult(SubTable.Name, requestInfo, LogDeep + 1, Token);
|
|
}
|
|
else
|
|
{
|
|
TableCollector.DataCollector.errorResultCount++;
|
|
LogEntry($"Could not convert result of NXQL query for subtable {SubTable.Name} on engine {Server.Name}({Server.Address}) to csv.", LogLevels.Error);
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
public async Task ErrorProcessing(string Content, CancellationToken Token)
|
|
{
|
|
TableCollector.DataCollector.errorResultCount++;
|
|
await Task.CompletedTask;
|
|
LogEntry($"Could not get result of NXQL query for subtable {SubTable.Name} on engine {Server.Name}({Server.Address})", LogLevels.Error);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
public class cNxqlColumnInfo
|
|
{
|
|
public cDataHistoryConfigColumnBase Column;
|
|
public int ColIndex;
|
|
public string StaticValue;
|
|
}
|
|
|
|
public class cNxqlAggregationEntry
|
|
{
|
|
internal cDataHistoryConfigColumn Column;
|
|
|
|
internal object LatestValue = null;
|
|
internal DateTime? LastestEvent = null;
|
|
|
|
internal Int64? aggInt = null;
|
|
internal Double? aggDouble = null;
|
|
internal Double? weightDouble = null;
|
|
internal String aggString = null;
|
|
internal List<string> listString = null;
|
|
internal Boolean? aggBoolean = null;
|
|
internal DateTime? aggEvent = null;
|
|
internal DateTime? aggTime = null;
|
|
internal Version aggVersion = null;
|
|
|
|
internal cNxqlAggregationEntry(cDataHistoryConfigColumn Column)
|
|
{
|
|
this.Column = Column;
|
|
}
|
|
|
|
private static string getStingList(List<string> L)
|
|
{
|
|
if (L == null || L.Count == 0)
|
|
return null;
|
|
|
|
var RetVal = "";
|
|
foreach (var Entry in L)
|
|
{
|
|
if (string.IsNullOrEmpty(Entry))
|
|
continue;
|
|
if (RetVal != "")
|
|
RetVal += ',';
|
|
RetVal += Entry;
|
|
}
|
|
return RetVal;
|
|
}
|
|
|
|
internal void addValue(object Value, DateTime? EventTime2)
|
|
{
|
|
try
|
|
{
|
|
DateTime EventTime;
|
|
if (EventTime2 == null)
|
|
EventTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
|
else
|
|
EventTime = (DateTime)EventTime2;
|
|
|
|
switch (Column.AggregationType)
|
|
{
|
|
case eDataHistoryAggregationType.average:
|
|
if (Value != null)
|
|
{
|
|
switch (Column.ValueType)
|
|
{
|
|
case enumFasdValueType.INT:
|
|
case enumFasdValueType.BIGINT:
|
|
case enumFasdValueType.FLOAT:
|
|
var val01 = Convert.ToDouble(Value);
|
|
if (weightDouble == null)
|
|
weightDouble = 1;
|
|
else
|
|
weightDouble++;
|
|
if (aggDouble == null)
|
|
aggDouble = val01;
|
|
else
|
|
aggDouble += val01;
|
|
return;
|
|
}
|
|
}
|
|
return;
|
|
case eDataHistoryAggregationType.latestTime:
|
|
if (aggEvent == null || aggEvent < EventTime)
|
|
aggEvent = EventTime;
|
|
return;
|
|
case eDataHistoryAggregationType.count:
|
|
if (aggInt == null)
|
|
aggInt = 1;
|
|
else
|
|
aggInt++;
|
|
return;
|
|
case eDataHistoryAggregationType.valuecount:
|
|
if (Value != null)
|
|
{
|
|
if (aggInt == null)
|
|
aggInt = 1;
|
|
else
|
|
aggInt++;
|
|
}
|
|
return;
|
|
}
|
|
|
|
switch (Column.ValueType)
|
|
{
|
|
case enumFasdValueType.INT:
|
|
case enumFasdValueType.BIGINT:
|
|
if (Value != null)
|
|
{
|
|
var val02 = Convert.ToInt64(Value);
|
|
switch (Column.AggregationType)
|
|
{
|
|
case eDataHistoryAggregationType.min:
|
|
if (aggInt == null)
|
|
aggInt = val02;
|
|
else
|
|
aggInt = Math.Min((Int64)aggInt, val02);
|
|
return;
|
|
case eDataHistoryAggregationType.max:
|
|
if (aggInt == null)
|
|
aggInt = val02;
|
|
else
|
|
aggInt = Math.Max((Int64)aggInt, val02);
|
|
return;
|
|
case eDataHistoryAggregationType.sum:
|
|
if (aggInt == null)
|
|
aggInt = val02;
|
|
else
|
|
aggInt += val02;
|
|
return;
|
|
case eDataHistoryAggregationType.first:
|
|
case eDataHistoryAggregationType.Unknown:
|
|
if (aggEvent == null)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggInt = val02;
|
|
}
|
|
else
|
|
{
|
|
if (EventTime < aggEvent)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggInt = val02;
|
|
}
|
|
}
|
|
return;
|
|
case eDataHistoryAggregationType.latestValue:
|
|
if (aggEvent == null)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggInt = val02;
|
|
}
|
|
else
|
|
{
|
|
if (EventTime >= aggEvent)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggInt = val02;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
return;
|
|
case enumFasdValueType.FLOAT:
|
|
if (Value != null)
|
|
{
|
|
var val03 = Convert.ToDouble(Value);
|
|
switch (Column.AggregationType)
|
|
{
|
|
case eDataHistoryAggregationType.min:
|
|
if (aggDouble == null)
|
|
aggDouble = val03;
|
|
else
|
|
aggDouble = Math.Min((Double)aggDouble, val03);
|
|
return;
|
|
case eDataHistoryAggregationType.max:
|
|
if (aggDouble == null)
|
|
aggDouble = val03;
|
|
else
|
|
aggDouble = Math.Max((Double)aggDouble, val03);
|
|
return;
|
|
case eDataHistoryAggregationType.sum:
|
|
if (aggDouble == null)
|
|
aggDouble = val03;
|
|
else
|
|
aggDouble += val03;
|
|
return;
|
|
case eDataHistoryAggregationType.first:
|
|
case eDataHistoryAggregationType.Unknown:
|
|
if (aggEvent == null)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggDouble = val03;
|
|
}
|
|
else
|
|
{
|
|
if (EventTime < aggEvent)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggDouble = val03;
|
|
}
|
|
}
|
|
return;
|
|
case eDataHistoryAggregationType.latestValue:
|
|
if (aggEvent == null)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggDouble = val03;
|
|
}
|
|
else
|
|
{
|
|
if (EventTime >= aggEvent)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggDouble = val03;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
return;
|
|
case enumFasdValueType.DATETIME:
|
|
if (Value != null)
|
|
{
|
|
var val04 = Convert.ToDateTime(Value);
|
|
switch (Column.AggregationType)
|
|
{
|
|
case eDataHistoryAggregationType.min:
|
|
if (aggTime == null)
|
|
aggTime = val04;
|
|
else if (val04 < aggTime)
|
|
aggTime = val04;
|
|
return;
|
|
case eDataHistoryAggregationType.max:
|
|
if (aggTime == null)
|
|
aggTime = val04;
|
|
else if (val04 > aggTime)
|
|
aggTime = val04;
|
|
return;
|
|
case eDataHistoryAggregationType.sum:
|
|
return;
|
|
case eDataHistoryAggregationType.first:
|
|
case eDataHistoryAggregationType.Unknown:
|
|
if (aggEvent == null)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggTime = val04;
|
|
}
|
|
else
|
|
{
|
|
if (EventTime < aggEvent)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggTime = val04;
|
|
}
|
|
}
|
|
return;
|
|
case eDataHistoryAggregationType.latestValue:
|
|
if (aggEvent == null)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggTime = val04;
|
|
}
|
|
else
|
|
{
|
|
if (EventTime >= aggEvent)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggTime = val04;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
return;
|
|
case enumFasdValueType.VERSION:
|
|
if (Value != null && Version.TryParse(Value.ToString(), out var val05))
|
|
{
|
|
switch (Column.AggregationType)
|
|
{
|
|
case eDataHistoryAggregationType.min:
|
|
if (aggVersion == null)
|
|
aggVersion = val05;
|
|
else if (val05 < aggVersion)
|
|
aggVersion = val05;
|
|
return;
|
|
case eDataHistoryAggregationType.max:
|
|
if (aggVersion == null)
|
|
aggVersion = val05;
|
|
else if (val05 > aggVersion)
|
|
aggVersion = val05;
|
|
return;
|
|
case eDataHistoryAggregationType.sum:
|
|
var strVal05 = val05.ToString();
|
|
if (listString == null)
|
|
listString = new List<string>() { strVal05 };
|
|
else if (listString.Count < 32 && !listString.Contains(strVal05))
|
|
listString.Add(strVal05);
|
|
return;
|
|
case eDataHistoryAggregationType.first:
|
|
case eDataHistoryAggregationType.Unknown:
|
|
if (aggEvent == null)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggVersion = val05;
|
|
}
|
|
else
|
|
{
|
|
if (EventTime < aggEvent)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggVersion = val05;
|
|
}
|
|
}
|
|
return;
|
|
case eDataHistoryAggregationType.latestValue:
|
|
if (aggEvent == null)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggVersion = val05;
|
|
}
|
|
else
|
|
{
|
|
if (EventTime >= aggEvent)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggVersion = val05;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
return;
|
|
case enumFasdValueType.BOOLEAN:
|
|
if (Value == null)
|
|
return;
|
|
|
|
bool val06 = false;
|
|
if (Value is bool v)
|
|
val06 = v;
|
|
else if (Value is string val06Str)
|
|
{
|
|
switch (val06Str.Trim().ToLower())
|
|
{
|
|
case "1":
|
|
case "true":
|
|
case "yes":
|
|
val06 = true;
|
|
break;
|
|
case "0":
|
|
case "false":
|
|
case "no":
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
else if (Value is sbyte
|
|
|| Value is byte
|
|
|| Value is short
|
|
|| Value is ushort
|
|
|| Value is int
|
|
|| Value is uint
|
|
|| Value is long
|
|
|| Value is ulong)
|
|
{
|
|
var val06Int = Convert.ToInt64(Value);
|
|
if (val06Int == 0) { }
|
|
else if (val06Int == 1)
|
|
val06 = true;
|
|
return;
|
|
}
|
|
else if (Value is float
|
|
|| Value is double
|
|
|| Value is decimal)
|
|
{
|
|
var val06Double = Convert.ToDouble(Value);
|
|
if (val06Double == 0) { }
|
|
else if (val06Double == 1)
|
|
val06 = true;
|
|
else
|
|
return;
|
|
}
|
|
else
|
|
return;
|
|
|
|
switch (Column.AggregationType)
|
|
{
|
|
case eDataHistoryAggregationType.min:
|
|
if (aggBoolean == null)
|
|
aggBoolean = val06;
|
|
else
|
|
aggBoolean &= val06;
|
|
return;
|
|
case eDataHistoryAggregationType.max:
|
|
if (aggBoolean == null)
|
|
aggBoolean = val06;
|
|
else
|
|
aggBoolean |= val06;
|
|
return;
|
|
case eDataHistoryAggregationType.sum:
|
|
return;
|
|
case eDataHistoryAggregationType.first:
|
|
case eDataHistoryAggregationType.Unknown:
|
|
if (aggEvent == null)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggBoolean = val06;
|
|
}
|
|
else
|
|
{
|
|
if (EventTime < aggEvent)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggBoolean = val06;
|
|
}
|
|
}
|
|
return;
|
|
case eDataHistoryAggregationType.latestValue:
|
|
if (aggEvent == null)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggBoolean = val06;
|
|
}
|
|
else
|
|
{
|
|
if (EventTime >= aggEvent)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggBoolean = val06;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
return;
|
|
default:
|
|
if (Value == null)
|
|
return;
|
|
var val07 = Value.ToString();
|
|
if (val07 == "")
|
|
return;
|
|
switch (Column.AggregationType)
|
|
{
|
|
case eDataHistoryAggregationType.sum:
|
|
if (listString == null)
|
|
listString = new List<string>() { val07 };
|
|
else if (listString.Count < 32 && !listString.Contains(val07))
|
|
listString.Add(val07);
|
|
return;
|
|
case eDataHistoryAggregationType.first:
|
|
case eDataHistoryAggregationType.Unknown:
|
|
if (aggEvent == null)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggString = val07;
|
|
}
|
|
else
|
|
{
|
|
if (EventTime < aggEvent)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggString = val07;
|
|
}
|
|
}
|
|
return;
|
|
case eDataHistoryAggregationType.latestValue:
|
|
if (aggEvent == null)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggString = val07;
|
|
}
|
|
else
|
|
{
|
|
if (EventTime >= aggEvent)
|
|
{
|
|
aggEvent = EventTime;
|
|
aggString = val07;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
}
|
|
|
|
public object getValue()
|
|
{
|
|
try
|
|
{
|
|
switch (Column.AggregationType)
|
|
{
|
|
case eDataHistoryAggregationType.average:
|
|
switch (Column.ValueType)
|
|
{
|
|
case enumFasdValueType.INT:
|
|
case enumFasdValueType.BIGINT:
|
|
case enumFasdValueType.FLOAT:
|
|
if (weightDouble == 0 || aggDouble == null)
|
|
return null;
|
|
else
|
|
return (aggDouble / weightDouble);
|
|
}
|
|
return null;
|
|
case eDataHistoryAggregationType.latestTime:
|
|
return aggEvent;
|
|
case eDataHistoryAggregationType.count:
|
|
case eDataHistoryAggregationType.valuecount:
|
|
return aggInt;
|
|
}
|
|
|
|
switch (Column.ValueType)
|
|
{
|
|
case enumFasdValueType.INT:
|
|
case enumFasdValueType.BIGINT:
|
|
switch (Column.AggregationType)
|
|
{
|
|
case eDataHistoryAggregationType.min:
|
|
case eDataHistoryAggregationType.max:
|
|
case eDataHistoryAggregationType.sum:
|
|
case eDataHistoryAggregationType.first:
|
|
case eDataHistoryAggregationType.latestValue:
|
|
case eDataHistoryAggregationType.Unknown:
|
|
if (Column.ValueType == enumFasdValueType.INT)
|
|
return Convert.ToInt32(aggInt);
|
|
return aggInt;
|
|
}
|
|
return null;
|
|
case enumFasdValueType.FLOAT:
|
|
switch (Column.AggregationType)
|
|
{
|
|
case eDataHistoryAggregationType.min:
|
|
case eDataHistoryAggregationType.max:
|
|
case eDataHistoryAggregationType.sum:
|
|
case eDataHistoryAggregationType.first:
|
|
case eDataHistoryAggregationType.latestValue:
|
|
case eDataHistoryAggregationType.Unknown:
|
|
return Convert.ToSingle(aggDouble);
|
|
}
|
|
return null;
|
|
case enumFasdValueType.DATETIME:
|
|
switch (Column.AggregationType)
|
|
{
|
|
case eDataHistoryAggregationType.min:
|
|
case eDataHistoryAggregationType.max:
|
|
case eDataHistoryAggregationType.first:
|
|
case eDataHistoryAggregationType.latestValue:
|
|
case eDataHistoryAggregationType.Unknown:
|
|
return aggTime;
|
|
}
|
|
return null;
|
|
case enumFasdValueType.VERSION:
|
|
switch (Column.AggregationType)
|
|
{
|
|
case eDataHistoryAggregationType.min:
|
|
case eDataHistoryAggregationType.max:
|
|
case eDataHistoryAggregationType.first:
|
|
case eDataHistoryAggregationType.latestValue:
|
|
case eDataHistoryAggregationType.Unknown:
|
|
return aggVersion;
|
|
case eDataHistoryAggregationType.sum:
|
|
return getStingList(listString);
|
|
}
|
|
return null;
|
|
case enumFasdValueType.BOOLEAN:
|
|
switch (Column.AggregationType)
|
|
{
|
|
case eDataHistoryAggregationType.min:
|
|
case eDataHistoryAggregationType.max:
|
|
case eDataHistoryAggregationType.first:
|
|
case eDataHistoryAggregationType.latestValue:
|
|
case eDataHistoryAggregationType.Unknown:
|
|
return aggBoolean;
|
|
}
|
|
return null;
|
|
default:
|
|
switch (Column.AggregationType)
|
|
{
|
|
case eDataHistoryAggregationType.first:
|
|
case eDataHistoryAggregationType.latestValue:
|
|
case eDataHistoryAggregationType.Unknown:
|
|
return aggString;
|
|
case eDataHistoryAggregationType.sum:
|
|
return getStingList(listString);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
}
|