From 2770ebbb6db4a789a87fce451f980d12cce2f159 Mon Sep 17 00:00:00 2001 From: "Drechsler, Meik" Date: Wed, 22 Jul 2026 11:57:15 +0200 Subject: [PATCH] Refactor Citrix virtual session data collection --- .../F4SD-DataClusters-Configuration.xml | 73 ++ .../Config/F4SD-HealthCard-Configuration.xml | 2 +- .../DataHistorySql.xml | 224 +++--- C4IT_DataHistoryProvider_Test/ctrlCitrix.cs | 58 -- .../ctrlTestFunctions.Designer.cs | 42 +- .../ctrlTestFunctions.cs | 38 +- .../DataHistoryCollector.cs | 89 +-- .../Citrix/DataHistoryCollectorCitrix.cs | 734 ++++++------------ .../F4SD-DataClusters-Configuration.xml | 73 ++ .../Config/F4SD-HealthCard-Configuration.xml | 2 +- .../F4SD-DataClusters-Configuration.xml | 73 ++ .../Config/F4SD-HealthCard-Configuration.xml | 2 +- 12 files changed, 651 insertions(+), 759 deletions(-) diff --git a/C4IT_DataHistoryProvider_Test/Config/F4SD-DataClusters-Configuration.xml b/C4IT_DataHistoryProvider_Test/Config/F4SD-DataClusters-Configuration.xml index fee0b08..d6824a0 100644 --- a/C4IT_DataHistoryProvider_Test/Config/F4SD-DataClusters-Configuration.xml +++ b/C4IT_DataHistoryProvider_Test/Config/F4SD-DataClusters-Configuration.xml @@ -1338,6 +1338,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ diff --git a/C4IT_DataHistoryProvider_Test/Config/F4SD-HealthCard-Configuration.xml b/C4IT_DataHistoryProvider_Test/Config/F4SD-HealthCard-Configuration.xml index 817c258..479942d 100644 --- a/C4IT_DataHistoryProvider_Test/Config/F4SD-HealthCard-Configuration.xml +++ b/C4IT_DataHistoryProvider_Test/Config/F4SD-HealthCard-Configuration.xml @@ -1530,7 +1530,7 @@ Benutzer-Informationen - + Benutzername diff --git a/C4IT_DataHistoryProvider_Test/DataHistorySql.xml b/C4IT_DataHistoryProvider_Test/DataHistorySql.xml index b244523..7325230 100644 --- a/C4IT_DataHistoryProvider_Test/DataHistorySql.xml +++ b/C4IT_DataHistoryProvider_Test/DataHistorySql.xml @@ -680,49 +680,49 @@ SELECT @cnt - - DECLARE @cnt INT - SET @cnt = 0 + + DECLARE @cnt INT + SET @cnt = 0 - IF EXISTS(SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[main-user]') AND type in (N'U')) - AND NOT EXISTS (SELECT * FROM Information_Schema.Columns WHERE Table_Name = 'main-user' AND Column_Name = 'id_citrix') - BEGIN - PRINT N'Start adding column [id_citrix] to table [main-user] ...'; - ALTER Table [main-user] ADD [id_citrix] int NULL; - SET @cnt = @cnt + 1; - PRINT N'... done.' - END + IF EXISTS(SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[main-user]') AND type in (N'U')) + AND NOT EXISTS (SELECT * FROM Information_Schema.Columns WHERE Table_Name = 'main-user' AND Column_Name = 'id_citrix') + BEGIN + PRINT N'Start adding column [id_citrix] to table [main-user] ...'; + ALTER Table [main-user] ADD [id_citrix] int NULL; + SET @cnt = @cnt + 1; + PRINT N'... done.' + END - IF EXISTS(SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[main-user]') AND type in (N'U')) - AND NOT EXISTS (SELECT * FROM Information_Schema.Columns WHERE Table_Name = 'main-user' AND Column_Name = 'tenant_citrix') - BEGIN - PRINT N'Start adding column [tenant_citrix] to table [main-user] ...'; - ALTER Table [main-user] ADD [tenant_citrix] nvarchar(100) NULL; - SET @cnt = @cnt + 1; - PRINT N'... done.' - END + IF EXISTS(SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[main-user]') AND type in (N'U')) + AND NOT EXISTS (SELECT * FROM Information_Schema.Columns WHERE Table_Name = 'main-user' AND Column_Name = 'tenant_citrix') + BEGIN + PRINT N'Start adding column [tenant_citrix] to table [main-user] ...'; + ALTER Table [main-user] ADD [tenant_citrix] nvarchar(100) NULL; + SET @cnt = @cnt + 1; + PRINT N'... done.' + END - IF EXISTS(SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[QActions]') AND type in (N'U')) - AND NOT EXISTS (SELECT * FROM Information_Schema.Columns WHERE Table_Name = 'QActions' AND Column_Name = 'TimeStamp') - BEGIN - PRINT N'Start adding column [TimeStamp] to table [QActions] ...'; - ALTER Table [QActions] ADD [TimeStamp] Datetime NULL; - SET @cnt = @cnt + 1; - PRINT N'... done.' - END + IF EXISTS(SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[QActions]') AND type in (N'U')) + AND NOT EXISTS (SELECT * FROM Information_Schema.Columns WHERE Table_Name = 'QActions' AND Column_Name = 'TimeStamp') + BEGIN + PRINT N'Start adding column [TimeStamp] to table [QActions] ...'; + ALTER Table [QActions] ADD [TimeStamp] Datetime NULL; + SET @cnt = @cnt + 1; + PRINT N'... done.' + END - IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'IX_main-user_upn_external') - BEGIN + IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'IX_main-user_upn_external') + BEGIN - CREATE NONCLUSTERED INDEX [IX_main-user_upn_external] ON [main-user] ([upn_external] ASC); + CREATE NONCLUSTERED INDEX [IX_main-user_upn_external] ON [main-user] ([upn_external] ASC); - SET @cnt = @cnt+1; - PRINT N'... done.' + SET @cnt = @cnt+1; + PRINT N'... done.' - END + END - SELECT @cnt - + SELECT @cnt + DECLARE @cnt INT @@ -943,12 +943,12 @@ UPDATE [main-computer] SET [id_azure]=@id, [deviceid_azure]=@deviceid, tenant_azure = @tenant WHERE [sid_bin]=@sid_bin - + UPDATE [main-computer] SET [id_intune]=@id WHERE [deviceId_azure]=@deviceId - - UPDATE [main-computer] SET [id_intune]=@id, [deviceid_azure]=@deviceid, tenant_azure = @tenant WHERE upn_internal = @upn_internal and [domainNB] is null + + UPDATE [main-computer] SET [id_intune]=@id, [deviceid_azure]=@deviceid, tenant_azure = @tenant WHERE upn_internal = @upn_internal and [domainNB] is null DECLARE @ID UNIQUEIDENTIFIER @@ -967,8 +967,8 @@ UPDATE [main-user] SET [id_citrix]=@id WHERE [sid_bin]=@sid_bin - - UPDATE [main-user] SET [id_citrix]=@id WHERE upn_internal = @upn_internal AND [id_citrix] is null + + UPDATE [main-user] SET [id_citrix]=@id WHERE upn_internal = @upn_internal AND [id_citrix] is null UPDATE [main-user] SET [id_agent]=@id_agent WHERE [sid_bin]=@sid_bin AND ([id_agent] is null OR [id_agent] != @id_agent) @@ -1011,11 +1011,11 @@ SELECT [id] FROM [main-user] WHERE [account] like @account AND ([domainNB] like @domain OR [domainDns] like @domain) - SELECT [sid_bin], [id_agent], [Name], [last_seen_by_agent], NULL , [tenant_azure],[id_intune], [deviceId_azure],null,null FROM [main-computer] WHERE id = @id - + SELECT [sid_bin], [id_agent], [Name], [last_seen_by_agent], NULL , [tenant_azure],[id_intune], [deviceId_azure],null,null FROM [main-computer] WHERE id = @id + - SELECT [sid_bin], [id_agent], [account], [last_seen_by_agent], [id_agent_azure], [tenant_azure], [id_azure],null,[id_citrix],[tenant_citrix] FROM [main-user] WHERE id = @id - + SELECT [sid_bin], [id_agent], [account], [last_seen_by_agent], [id_agent_azure], [tenant_azure], [id_azure],null,[id_citrix],[tenant_citrix] FROM [main-user] WHERE id = @id + SELECT [id], [full_name], [is_active], [account], COALESCE([domainDns], [domainNB] ), CASE WHEN [id_agent] <> @id THEN 1 ELSE 0 END FROM [main-user] WHERE [id_agent] = @id OR [id_agent_azure] = @id @@ -1675,22 +1675,22 @@ PRINT N' ...done.'; END - - IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[Tickets]') AND type in (N'U')) + + IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[Tickets]') AND type in (N'U')) BEGIN PRINT N'start creating table [Tickets]...'; - CREATE TABLE [Tickets]( - [TicketId] [uniqueidentifier] NOT NULL, - [Status] [nvarchar](16) NOT NULL, - [Start] [datetime] NULL, - [Finish] [datetime] NULL, - [ActiveTime] [int] NULL, - [UserId] [uniqueidentifier] NOT NULL, - [CaseId] [uniqueidentifier] NOT NULL - ) + CREATE TABLE [Tickets]( + [TicketId] [uniqueidentifier] NOT NULL, + [Status] [nvarchar](16) NOT NULL, + [Start] [datetime] NULL, + [Finish] [datetime] NULL, + [ActiveTime] [int] NULL, + [UserId] [uniqueidentifier] NOT NULL, + [CaseId] [uniqueidentifier] NOT NULL + ) - PRINT N' ...done.'; + PRINT N' ...done.'; END IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[QActions]') AND type in (N'U')) @@ -1831,20 +1831,20 @@ CREATE NONCLUSTERED INDEX [IX_UserSession_SessionId] ON [UserSessions] ([SessionId] ASC) INCLUDE([UserId]) WITH (DROP_EXISTING = OFF) ON [PRIMARY]; - + CREATE NONCLUSTERED INDEX [IX_Cases_CaseId] ON [Cases] ([CaseId] ASC) INCLUDE(SessionId) WITH (DROP_EXISTING = OFF) ON [PRIMARY]; - - + + CREATE NONCLUSTERED INDEX [IX_CaseTime_CaseId] ON [CaseTime] ([CaseId] ASC) INCLUDE(StatusId) WITH (DROP_EXISTING = OFF) ON [PRIMARY]; - + CREATE NONCLUSTERED INDEX [IX_QActionExecutions_ExecutionId_CaseId] ON [QActionExecutions] ([ExecutionId],[CaseId] ASC) WITH (DROP_EXISTING = OFF) ON [PRIMARY]; - + CREATE NONCLUSTERED INDEX [IX_Tickets_TicketId_CaseId] ON [dbo].[Cases] ([TicketId] ASC, [CaseId] ASC) WITH (DROP_EXISTING = OFF) ON [PRIMARY]; - + END @@ -1886,8 +1886,8 @@ SET @cnt = @cnt+1; PRINT N'... done.' - END - + END + IF EXISTS (SELECT 1 FROM sys.objects WHERE name = 'FK_Cases_SessionId' AND type='F') BEGIN ALTER TABLE Cases DROP CONSTRAINT FK_Cases_SessionId @@ -2460,67 +2460,67 @@ - IF EXISTS (SELECT TOP 1 * FROM UserSessions WHERE SessionId = @SessionId) - BEGIN - UPDATE [UserSessions] - SET [Finish] = @SessionDate - WHERE SessionId = @SessionId; - END - - IF NOT EXISTS (SELECT TOP 1 * FROM UserSessions WHERE SessionId = @SessionId) AND EXISTS (SELECT TOP 1 * FROM dbo.Cases WHERE SessionId = @SessionId) - BEGIN - DECLARE @CurrentTimeStamp DATETIME = GETUTCDATE(); - INSERT INTO UserSessions - ( UserId, SessionId, [Start], [Finish] ) - SELECT TOP 1 UserId,SessionId,[Start],@CurrentTimeStamp FROM Cases - WHERE SessionId = @SessionId ORDER BY [Start] ASC; - END + IF EXISTS (SELECT TOP 1 * FROM UserSessions WHERE SessionId = @SessionId) + BEGIN + UPDATE [UserSessions] + SET [Finish] = @SessionDate + WHERE SessionId = @SessionId; + END - IF EXISTS (SELECT TOP 1 * FROM dbo.Cases WHERE SessionId = @SessionId) - BEGIN - INSERT INTO [CaseTime] - ( CaseId, StatusId, [TimeStamp] ) - SELECT CaseId, 4, @SessionDate - FROM dbo.Cases - WHERE SessionId = @SessionId AND Finish IS NULL; + IF NOT EXISTS (SELECT TOP 1 * FROM UserSessions WHERE SessionId = @SessionId) AND EXISTS (SELECT TOP 1 * FROM dbo.Cases WHERE SessionId = @SessionId) + BEGIN + DECLARE @CurrentTimeStamp DATETIME = GETUTCDATE(); + INSERT INTO UserSessions + ( UserId, SessionId, [Start], [Finish] ) + SELECT TOP 1 UserId,SessionId,[Start],@CurrentTimeStamp FROM Cases + WHERE SessionId = @SessionId ORDER BY [Start] ASC; + END - UPDATE [Cases] - SET [Finish] = @SessionDate - WHERE SessionId = @SessionId AND Finish IS NULL; - END + IF EXISTS (SELECT TOP 1 * FROM dbo.Cases WHERE SessionId = @SessionId) + BEGIN + INSERT INTO [CaseTime] + ( CaseId, StatusId, [TimeStamp] ) + SELECT CaseId, 4, @SessionDate + FROM dbo.Cases + WHERE SessionId = @SessionId AND Finish IS NULL; - + UPDATE [Cases] + SET [Finish] = @SessionDate + WHERE SessionId = @SessionId AND Finish IS NULL; + END + + DECLARE @CurrentTimeStamp DATETIME = GETUTCDATE(); IF EXISTS (SELECT TOP 1 * FROM dbo.Cases JOIN dbo.CaseTime ON Cases.CaseId = CaseTime.CaseId WHERE Cases.CaseId = @CaseId AND CaseTime.StatusId = 4 ) BEGIN - UPDATE [CaseTime] - SET Statusid = 2 - FROM dbo.Cases - JOIN dbo.CaseTime ON Cases.CaseId = CaseTime.CaseId - WHERE Cases.CaseId = @CaseId AND - CaseTime.StatusId = 4 + UPDATE [CaseTime] + SET Statusid = 2 + FROM dbo.Cases + JOIN dbo.CaseTime ON Cases.CaseId = CaseTime.CaseId + WHERE Cases.CaseId = @CaseId AND + CaseTime.StatusId = 4 - INSERT INTO [CaseTime] - ( CaseId, StatusId, [TimeStamp] ) - SELECT @CaseId, 3, @CurrentTimeStamp; + INSERT INTO [CaseTime] + ( CaseId, StatusId, [TimeStamp] ) + SELECT @CaseId, 3, @CurrentTimeStamp; - UPDATE [Cases] - SET [Finish] = NULL - WHERE CaseId = @CaseId; + UPDATE [Cases] + SET [Finish] = NULL + WHERE CaseId = @CaseId; END - + - IF NOT EXISTS (SELECT [TicketId], [UserId],[CaseId],[Status],[Start], [Finish],ActiveTime FROM [Tickets] - WHERE [TicketId] = @TicketId AND [UserId] = @UserId AND [CaseId] = @CaseId AND [Status] = @Status AND [Start] = @Start AND [Finish] = @Finish AND ActiveTime = @ActiveTime ) - BEGIN - INSERT INTO [Tickets] - ([TicketId], [UserId],[CaseId],[Status],[Start], [Finish],ActiveTime) - SELECT @TicketId,@UserId,@CaseId,@Status,@Start,@Finish,@ActiveTime; - END - + IF NOT EXISTS (SELECT [TicketId], [UserId],[CaseId],[Status],[Start], [Finish],ActiveTime FROM [Tickets] + WHERE [TicketId] = @TicketId AND [UserId] = @UserId AND [CaseId] = @CaseId AND [Status] = @Status AND [Start] = @Start AND [Finish] = @Finish AND ActiveTime = @ActiveTime ) + BEGIN + INSERT INTO [Tickets] + ([TicketId], [UserId],[CaseId],[Status],[Start], [Finish],ActiveTime) + SELECT @TicketId,@UserId,@CaseId,@Status,@Start,@Finish,@ActiveTime; + END + INSERT INTO [QActionExecutions] diff --git a/C4IT_DataHistoryProvider_Test/ctrlCitrix.cs b/C4IT_DataHistoryProvider_Test/ctrlCitrix.cs index 75838a3..784fc96 100644 --- a/C4IT_DataHistoryProvider_Test/ctrlCitrix.cs +++ b/C4IT_DataHistoryProvider_Test/ctrlCitrix.cs @@ -44,24 +44,6 @@ namespace C4IT_DataHistoryProvider_Test ctrlProtocolBox1.ProcessMessage($"Validating Citrix connections finished."); return; - - foreach (var _system in systems) - { - if (!(_system is cDataHistoryCitrixCloudTenant Tenant)) - continue; - - var result = await Program.Collector.CitrixCollector.GetCitrixCommunicationForSystemAsync(Tenant.TenantID, false); - if (result != null) - { - ctrlProtocolBox1.ProcessMessage($"Citrix connection for tenant {Tenant.TenantID} successful"); - } - else - { - ctrlProtocolBox1.ProcessMessage($"Citrix connection for tenant {Tenant.TenantID} failed"); - - } - } - } catch (Exception E) { @@ -91,26 +73,6 @@ namespace C4IT_DataHistoryProvider_Test Program.Collector.ProcessUiMessage += ctrlProtocolBox1.ProcessMessage; var _requestInfo = new cF4sdWebRequestInfo("btnConnectCitrix_Click", null); - var systems = Program.Collector.InfrastructureConfig.Citrix.Systems; - - - foreach (var _system in systems) - { - if (!(_system is cDataHistoryCitrixCloudTenant Tenant)) - continue; - - var result = await Program.Collector.CitrixCollector.GetCitrixUsersAsync(); - if (result) - { - ctrlProtocolBox1.ProcessMessage($"Citrix connection for tenant {Tenant.TenantID} successful"); - } - else - { - ctrlProtocolBox1.ProcessMessage($"Citrix connection for tenant {Tenant.TenantID} failed"); - - } - } - } catch (Exception E) { @@ -139,26 +101,6 @@ namespace C4IT_DataHistoryProvider_Test Program.Collector.CleanupUiMessageHandler(); Program.Collector.ProcessUiMessage += ctrlProtocolBox1.ProcessMessage; var _requestInfo = new cF4sdWebRequestInfo("btnGetSessions_Click", null); - var systems = Program.Collector.InfrastructureConfig.Citrix.Systems; - - - foreach (var _system in systems) - { - if (!(_system is cDataHistoryCitrixCloudTenant Tenant)) - continue; - - var result = await Program.Collector.CitrixCollector.GetCitrixSessionsAsync(); - if (result) - { - ctrlProtocolBox1.ProcessMessage($"Citrix connection for tenant {Tenant.TenantID} successful"); - } - else - { - ctrlProtocolBox1.ProcessMessage($"Citrix connection for tenant {Tenant.TenantID} failed"); - - } - } - } catch (Exception E) { diff --git a/C4IT_DataHistoryProvider_Test/ctrlTestFunctions.Designer.cs b/C4IT_DataHistoryProvider_Test/ctrlTestFunctions.Designer.cs index 23d00d4..7b47839 100644 --- a/C4IT_DataHistoryProvider_Test/ctrlTestFunctions.Designer.cs +++ b/C4IT_DataHistoryProvider_Test/ctrlTestFunctions.Designer.cs @@ -42,13 +42,15 @@ namespace C4IT_DataHistoryProvider_Test this.label4 = new System.Windows.Forms.Label(); this.buttonClear = new System.Windows.Forms.Button(); this.checkBoxFiltered = new System.Windows.Forms.CheckBox(); + this.comboBoxSessions = new System.Windows.Forms.ComboBox(); + this.label5 = new System.Windows.Forms.Label(); this.ctrlProtocolBox1 = new C4IT_DataHistoryProvider_Test.ctrlProtocolBox(); this.SuspendLayout(); // // buttonGetUserComputerActivities // this.buttonGetUserComputerActivities.Location = new System.Drawing.Point(554, 17); - this.buttonGetUserComputerActivities.Margin = new System.Windows.Forms.Padding(2); + this.buttonGetUserComputerActivities.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.buttonGetUserComputerActivities.Name = "buttonGetUserComputerActivities"; this.buttonGetUserComputerActivities.Size = new System.Drawing.Size(164, 32); this.buttonGetUserComputerActivities.TabIndex = 1; @@ -97,7 +99,7 @@ namespace C4IT_DataHistoryProvider_Test // buttonComputerUserActivities // this.buttonComputerUserActivities.Location = new System.Drawing.Point(554, 65); - this.buttonComputerUserActivities.Margin = new System.Windows.Forms.Padding(2); + this.buttonComputerUserActivities.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.buttonComputerUserActivities.Name = "buttonComputerUserActivities"; this.buttonComputerUserActivities.Size = new System.Drawing.Size(164, 32); this.buttonComputerUserActivities.TabIndex = 5; @@ -129,7 +131,7 @@ namespace C4IT_DataHistoryProvider_Test // this.buttonGetRawValues.Enabled = false; this.buttonGetRawValues.Location = new System.Drawing.Point(554, 118); - this.buttonGetRawValues.Margin = new System.Windows.Forms.Padding(2); + this.buttonGetRawValues.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.buttonGetRawValues.Name = "buttonGetRawValues"; this.buttonGetRawValues.Size = new System.Drawing.Size(164, 32); this.buttonGetRawValues.TabIndex = 9; @@ -150,7 +152,7 @@ namespace C4IT_DataHistoryProvider_Test // label4 // this.label4.AutoSize = true; - this.label4.Location = new System.Drawing.Point(760, 22); + this.label4.Location = new System.Drawing.Point(768, 22); this.label4.Name = "label4"; this.label4.Size = new System.Drawing.Size(63, 20); this.label4.TabIndex = 12; @@ -172,28 +174,50 @@ namespace C4IT_DataHistoryProvider_Test this.checkBoxFiltered.AutoSize = true; this.checkBoxFiltered.Checked = true; this.checkBoxFiltered.CheckState = System.Windows.Forms.CheckState.Checked; - this.checkBoxFiltered.Location = new System.Drawing.Point(840, 67); + this.checkBoxFiltered.Location = new System.Drawing.Point(840, 68); this.checkBoxFiltered.Name = "checkBoxFiltered"; - this.checkBoxFiltered.Size = new System.Drawing.Size(175, 24); + this.checkBoxFiltered.Size = new System.Drawing.Size(182, 24); this.checkBoxFiltered.TabIndex = 15; this.checkBoxFiltered.Text = "Only with current use"; this.checkBoxFiltered.UseVisualStyleBackColor = true; this.checkBoxFiltered.CheckedChanged += new System.EventHandler(this.checkBoxFiltered_CheckedChanged); // + // comboBoxSessions + // + this.comboBoxSessions.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBoxSessions.FormattingEnabled = true; + this.comboBoxSessions.Location = new System.Drawing.Point(1191, 18); + this.comboBoxSessions.Name = "comboBoxSessions"; + this.comboBoxSessions.Size = new System.Drawing.Size(328, 28); + this.comboBoxSessions.TabIndex = 18; + this.comboBoxSessions.SelectedValueChanged += new System.EventHandler(this.comboBoxSessions_SelectedValueChanged); + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(1090, 23); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(97, 20); + this.label5.TabIndex = 17; + this.label5.Text = "V. Sessions:"; + // // ctrlProtocolBox1 // this.ctrlProtocolBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.ctrlProtocolBox1.Location = new System.Drawing.Point(9, 169); + this.ctrlProtocolBox1.Margin = new System.Windows.Forms.Padding(2); this.ctrlProtocolBox1.Name = "ctrlProtocolBox1"; - this.ctrlProtocolBox1.Size = new System.Drawing.Size(1068, 79); + this.ctrlProtocolBox1.Size = new System.Drawing.Size(1510, 78); this.ctrlProtocolBox1.TabIndex = 16; // // ctrlTestFunctions // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.comboBoxSessions); + this.Controls.Add(this.label5); this.Controls.Add(this.checkBoxFiltered); this.Controls.Add(this.buttonClear); this.Controls.Add(this.comboBoxTickets); @@ -209,7 +233,7 @@ namespace C4IT_DataHistoryProvider_Test this.Controls.Add(this.buttonGetUserComputerActivities); this.Controls.Add(this.ctrlProtocolBox1); this.Name = "ctrlTestFunctions"; - this.Size = new System.Drawing.Size(1088, 268); + this.Size = new System.Drawing.Size(1530, 268); this.Load += new System.EventHandler(this.ctrlTestFunctions_Load); this.ResumeLayout(false); this.PerformLayout(); @@ -232,5 +256,7 @@ namespace C4IT_DataHistoryProvider_Test private System.Windows.Forms.Button buttonClear; private System.Windows.Forms.CheckBox checkBoxFiltered; private ctrlProtocolBox ctrlProtocolBox1; + private System.Windows.Forms.ComboBox comboBoxSessions; + private System.Windows.Forms.Label label5; } } diff --git a/C4IT_DataHistoryProvider_Test/ctrlTestFunctions.cs b/C4IT_DataHistoryProvider_Test/ctrlTestFunctions.cs index b9bfa65..b70bf21 100644 --- a/C4IT_DataHistoryProvider_Test/ctrlTestFunctions.cs +++ b/C4IT_DataHistoryProvider_Test/ctrlTestFunctions.cs @@ -54,6 +54,9 @@ namespace C4IT_DataHistoryProvider_Test if (Program.Collector.M42WpmCollector != null) await Program.Collector.M42WpmCollector.ValidateConnectionAsync(true, CancellationToken.None); + if (Program.Collector.CitrixCollector != null) + await Program.Collector.CitrixCollector.ValidateConnectionsAsync(CancellationToken.None); + } catch (Exception E) { @@ -117,20 +120,37 @@ namespace C4IT_DataHistoryProvider_Test ctrlProtocolBox1.ProcessMessage("no further information found."); else { - comboBoxTickets.Items.Clear(); ctrlProtocolBox1.ProcessMessage($"{RetVal.Count} related objects found:"); + + // fill the drop down boxes for the realtions found + comboBoxTickets.Items.Clear(); + comboBoxSessions.Items.Clear(); foreach (var Entry2 in RetVal) { - if (Entry2.Type == enumF4sdSearchResultClass.Ticket) - comboBoxTickets.Items.Add(Entry2); + switch(Entry2.Type) + { + case enumF4sdSearchResultClass.Ticket: + comboBoxTickets.Items.Add(Entry2); + break; + case enumF4sdSearchResultClass.VirtualSession: + if (!string.IsNullOrEmpty(Entry2.DisplayName)) + comboBoxSessions.Items.Add(Entry2); + break; + } + ctrlProtocolBox1.ProcessMessage($" {Entry2.Type} {Entry2.Name} {{{Entry2.id}}}:"); ctrlProtocolBox1.ProcessMessage($" LastUsed: {Entry2.LastUsed}"); ctrlProtocolBox1.ProcessMessage($" Usage weight: {Entry2.UsingLevel}"); foreach (var Entry3 in Entry2.Infos) ctrlProtocolBox1.ProcessMessage($" {Entry3.Key}: {Entry3.Value}"); } + if (comboBoxTickets.Items.Count > 0) comboBoxTickets.SelectedIndex = 0; + if (comboBoxSessions.Items.Count > 0) + comboBoxSessions.SelectedIndex = 0; + + } } catch (Exception E) @@ -196,6 +216,11 @@ namespace C4IT_DataHistoryProvider_Test GetTables(); } + private void comboBoxSessions_SelectedValueChanged(object sender, EventArgs e) + { + GetTables(); + } + private void GetTables() { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } @@ -204,13 +229,15 @@ namespace C4IT_DataHistoryProvider_Test bool HasUser = comboBoxUserSelect.SelectedItem != null; bool HasComputer = comboBoxComputerSelect.SelectedItem != null; bool HasTicket = comboBoxTickets.SelectedItem != null; + bool HasSession = comboBoxSessions.SelectedItem != null; var Tables = new List(); foreach (var table in Program.Collector.ClusterConfig.Tables.Values) { if ((HasUser && table.ParentCluster.InformationClass == C4IT.FASD.Base.enumFasdInformationClass.User) || (HasComputer && table.ParentCluster.InformationClass == C4IT.FASD.Base.enumFasdInformationClass.Computer) || - (HasTicket && table.ParentCluster.InformationClass == C4IT.FASD.Base.enumFasdInformationClass.Ticket)) + (HasTicket && table.ParentCluster.InformationClass == C4IT.FASD.Base.enumFasdInformationClass.Ticket) || + (HasSession && table.ParentCluster.InformationClass == enumFasdInformationClass.VirtualSession)) { Tables.Add(table); } @@ -278,6 +305,8 @@ namespace C4IT_DataHistoryProvider_Test Identities.Add(new cF4sdIdentityEntry() { Class = enumFasdInformationClass.User, Id = UserEntry.id, AccountType = enumAccountType.AD }); if (comboBoxTickets.SelectedItem is cF4sdApiSearchResultRelation TicketEntry) Identities.Add(new cF4sdIdentityEntry() { Class = enumFasdInformationClass.Ticket, Id = TicketEntry.id }); + if (comboBoxSessions.SelectedItem is cF4sdApiSearchResultRelation SessionEntry) + Identities.Add(new cF4sdIdentityEntry() { Class = enumFasdInformationClass.VirtualSession, Id = SessionEntry.id, Instance = SessionEntry.Instance }); if (!(comboBoxTable.SelectedItem is cDataHistoryConfigTable Table)) return; @@ -467,5 +496,6 @@ namespace C4IT_DataHistoryProvider_Test } + } } diff --git a/F4SD-Cockpit-ServerCore/DataHistoryCollector.cs b/F4SD-Cockpit-ServerCore/DataHistoryCollector.cs index 4ee9172..3b1fe00 100644 --- a/F4SD-Cockpit-ServerCore/DataHistoryCollector.cs +++ b/F4SD-Cockpit-ServerCore/DataHistoryCollector.cs @@ -3825,77 +3825,42 @@ namespace C4IT.DataHistoryProvider try { - var Age2 = Age; - if (Ids.Count == 1) - Age2 = 365; - var AgentLimit = DateTime.UtcNow + TimeSpan.FromDays(-Age2); - - var idInput = Ids.Select(v => new cF4sdIdentityEntry() { Id = v, Class = infoClass }).ToList(); - var listIds = await getConnectorIdList(idInput, Token, requestInfo, LogDeep + 1); - - if (listIds == null || listIds.Count == 0) + var _relationService = GetStagedRelationService(); + if (_relationService == null) return null; - var lstAgentIds = new Dictionary(Ids.Count); - var lstTicketIds = new List(Ids.Count); - - foreach (var Entry in listIds) + var _relatedTo = new List(); + foreach (var _idEntry in Ids) { - if (M42WpmCollector != null && infoClass == enumFasdInformationClass.User) + var _rel = new cFasdApiSearchResultEntry() { - if (Entry.sid != null) - lstTicketIds.Add(Entry); - } - if (Entry.lastSeenByAgent != null && Entry.lastSeenByAgent >= AgentLimit) - { - if (Entry.agentId2 != null) - lstAgentIds[(int)Entry.agentId2] = new cDataHistoryUsageIdInfo() { Id = Entry.Id, AccountType = enumAccountType.Azure }; - if (Entry.agentId != null) - lstAgentIds[(int)Entry.agentId] = new cDataHistoryUsageIdInfo() { Id = Entry.Id, AccountType = enumAccountType.AD }; - } - if (Entry.localAgentIds != null) - { - foreach (var entryLocal in Entry.localAgentIds) - { - if (entryLocal.lastSeenByAgent >= AgentLimit) - lstAgentIds[entryLocal.agentId] = new cDataHistoryUsageIdInfo() { Id = Entry.Id, AccountType = enumAccountType.Local, account = entryLocal.account, domain = "" }; - } - } - } - - - if (DefaultLogger.IsDebug) - { - var _msgDebug = new List() - { - $"{lstAgentIds?.Count} agent ids found by usage determination:" + Type = cFasdApiSearchResultEntry.getSearchClass(infoClass), + id = _idEntry }; - foreach (var dbgEntry in lstAgentIds) - _msgDebug.Add($"{dbgEntry.Key}: accountType={dbgEntry.Value.AccountType}, account={dbgEntry.Value.account}, domain={dbgEntry.Value.domain}, id={dbgEntry.Value.Id}"); - DefaultLogger.LogList(LogLevels.Debug, _msgDebug); + _relatedTo.Add(_rel); } - var taskList = new List>>(2); - if (lstAgentIds.Count > 0) - taskList.Add(Task.Run>(async () => { return await GetUsedInfoFromAgentData(infoClass, lstAgentIds, Age, Token, requestInfo, LogDeep + 1); })); - if (lstTicketIds.Count > 0) - taskList.Add(Task.Run>(async () => { return await getTicketUsage(infoClass, lstTicketIds, Age, Token, requestInfo, LogDeep + 1); })); - - if (taskList.Count == 0) + var taskId = await _relationService.StartGatheringRelatedObjects(_relatedTo, Age); + if (Token.IsCancellationRequested) return null; - var arrTask = taskList.ToArray(); - var RetVal = await Task.WhenAll>(arrTask); - - var RetVal2 = new List(); - foreach (var Entry in RetVal) + var _retVal = new List(); + do { - if (Entry != null) - RetVal2.AddRange(Entry); - } + var _rels = await _relationService.GetRelatedObjectsAsync(taskId.Id, Token); + if (Token.IsCancellationRequested) + return null; + if (_rels == null) + return null; + + _retVal.AddRange(_rels.Relations); + + if (_rels.IsComplete) + break; + } while (true); + + return _retVal ; - if (RetVal2.Count > 0) - return RetVal2; } catch (Exception E) { @@ -5589,7 +5554,7 @@ namespace C4IT.DataHistoryProvider if (infoClass == enumFasdInformationClass.Ticket) return new cF4sdConnectorIds() { Id = Identity.Id, infoClass = enumFasdInformationClass.Ticket }; if (infoClass == enumFasdInformationClass.VirtualSession) - return new cF4sdConnectorIds() { Id = Identity.Id, infoClass = enumFasdInformationClass.VirtualSession }; + return new cF4sdConnectorIds() { Id = Identity.Id, Instance = Identity.Instance, infoClass = enumFasdInformationClass.VirtualSession }; if (infoClass == enumFasdInformationClass.MobileDevice) return new cF4sdConnectorIds() { Id = Identity.Id, infoClass = enumFasdInformationClass.MobileDevice }; @@ -6911,6 +6876,8 @@ namespace C4IT.DataHistoryProvider public enumFasdInformationClass infoClass { get; set; } = enumFasdInformationClass.Unknown; public Guid Id { get; set; } + public Guid Instance { get; set; } = Guid.Empty; + public int? agentId { get; set; } = null; public int? agentId2 { get; set; } = null; public List localAgentIds { get; set; } = null; diff --git a/F4SD-Cockpit-ServerCore/Modules/Citrix/DataHistoryCollectorCitrix.cs b/F4SD-Cockpit-ServerCore/Modules/Citrix/DataHistoryCollectorCitrix.cs index 2ff0717..ca808b8 100644 --- a/F4SD-Cockpit-ServerCore/Modules/Citrix/DataHistoryCollectorCitrix.cs +++ b/F4SD-Cockpit-ServerCore/Modules/Citrix/DataHistoryCollectorCitrix.cs @@ -42,40 +42,51 @@ namespace C4IT.DataHistoryProvider.Base.Modules.Citrix public const string constConnectorName = "Citrix Virtual Apps and Desktops connector"; public const string constLicenseId = "F2DA5E05-FD3C-4182-9164-9C722140BF85"; - public const string constCitrixUserList = "Users?$top={0}&$select=Id,Sid,Upn,Domain&$filter=Domain ne ''"; - public const string constCitrixSessionList = "Sessions?$expand=Machine($select=Id,DnsName,Name)&$select=SessionKey,StartDate,EndDate,ConnectionState"; - public const string constCitrixSessions = "Sessions?$filter=(EndDate eq null or EndDate gt {1}) and UserId in ({0})&$expand=Machine($select=Id,DnsName,Name)&$select=SessionKey,StartDate,EndDate,ConnectionState"; - public const string constCitrixSession = "Sessions?$filter=SessionKey eq {0}"; - public const string constCitrixSessionMetrics = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics"; - public const string constCitrixSessionMetricsIcaRTT = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,IcaRttMs)"; - public const string constCitrixSessionMetricsIcaLatency = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,IcaLatency)"; - public const string constCitrixSessionMetricsClientL7Latency = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,ClientL7Latency)"; - public const string constCitrixSessionMetricsServerL7Latency = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,ServerL7Latency)"; - public const string constCitrixSessionMetricsInputBandwidthUsed = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,InputBandwidthUsed)"; - public const string constCitrixSessionMetricsOutputBandwidthUsed = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,OutputBandwidthUsed)"; + // urls for static tables + public const string constCitrixSession = "Sessions?$filter=SessionKey eq {0}"; + public const string constCitrixSessionMachine = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=Machine($expand=CurrentLoadIndex,Catalog,DesktopGroup($select=Name)),Machine"; + public const string constCitrixSessionUser = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=User"; + public const string constCitrixSessionConnections = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=Connection"; + public const string constCitrixSessionCurrentConnection = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=CurrentConnection,LogOnMetrics"; + + // urls for historic tables + public const string constCitrixSessionMetrics = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics"; + + + // urls for detail tables + public const string constCitrixSessionMetricsIcaRTT = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,IcaRttMs)"; + public const string constCitrixSessionMetricsIcaLatency = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,IcaLatency)"; + public const string constCitrixSessionMetricsClientL7Latency = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,ClientL7Latency)"; + public const string constCitrixSessionMetricsServerL7Latency = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,ServerL7Latency)"; + public const string constCitrixSessionMetricsInputBandwidthUsed = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,InputBandwidthUsed)"; + public const string constCitrixSessionMetricsOutputBandwidthUsed = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,OutputBandwidthUsed)"; public const string constCitrixSessionMetricsOutputBandwidthAvailable = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,OutputBandwidthAvailable)"; - public const string constCitrixSessionMetricsFps = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,Fps)"; - public const string constCitrixSessionMetricsInputFps = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,InputFps)"; - public const string constCitrixSessionMetricsOutputFps = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,OutputFps)"; - public const string constCitrixSessionMetricsWanLatency = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,WanLatency)"; - public const string constCitrixSessionMetricsDcLatency = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,DcLatency)"; - public const string constCitrixSessionMachine = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=Machine($expand=CurrentLoadIndex,Catalog,DesktopGroup($select=Name)),Machine"; - public const string constCitrixSessionCurrentConnection = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=CurrentConnection,LogOnMetrics"; - public const string constCitrixSessionConnections = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=Connection"; - public const string constCitrixSessionLogInSummaries = "Sessions?$filter=UserId in ({0})$select=Id,StartDate&$expand=User($select=UserName),LogOnSummaries($select=LogOnDuration,AuthenticationDuration,BrokingDuration,ProfileLoadDuration,GpoProcessingDuration,InteractiveSessionDuration)"; - public const string constCitrixSessionLogInSummaries2 = "LogOnSummaries?$expand=Session($expand=User)&$filter=UserId eq {0}"; - public const string constCitrixUserConnections = "Sessions?$filter=UserId in ({0})&$expand=Connection"; - public const string constCitrixSessionUser = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=User"; + public const string constCitrixSessionMetricsFps = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,Fps)"; + public const string constCitrixSessionMetricsInputFps = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,InputFps)"; + public const string constCitrixSessionMetricsOutputFps = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,OutputFps)"; + public const string constCitrixSessionMetricsWanLatency = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,WanLatency)"; + public const string constCitrixSessionMetricsDcLatency = "Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,DcLatency)"; + public const string constCitrixSessionLogInSummaries = "Sessions?$filter=UserId in ({0})$select=Id,StartDate&$expand=User($select=UserName),LogOnSummaries($select=LogOnDuration,AuthenticationDuration,BrokingDuration,ProfileLoadDuration,GpoProcessingDuration,InteractiveSessionDuration)"; + public const string constCitrixUserConnections = "Sessions?$filter=UserId in ({0})&$expand=Connection"; + public const string constCitrixSessionLogInSummaries2 = "LogOnSummaries?$expand=Session($expand=User)&$filter=UserId eq {0}"; + public const string constCitrixSessionLogoff = "manage/Sessions/{0}/$logoff"; public const string constCitrixSessionHide = "manage/Sessions/{0}/$hide"; public const string constCitrixSessionSendMessage = "manage/Sessions/{0}/$sendMessage"; + + // static tables + public const string constTableNameSessionDetails = "citrix-session-details"; + public const string constTableNameSession = "citrix-session"; public const string constTableNameSessionMachine = "citrix-session-machine"; + public const string constTableNameSessionUser = "citrix-session-user"; public const string constTableNameSessionConnection = "citrix-session-connection"; public const string constTableNameSessionCurrentConnection = "citrix-session-currentConnection"; + + // history tables public const string constTableNameSessionMetrics = "citrix-session-metrics"; - public const string constTableNameSessionStateMetrics = "citrix-connectionState-metrics"; - public const string constTableNameSessionUser = "citrix-session-user"; + + // detail tables public const string constTableNameSessionDetailsIcaRttMS = "citrix-session-details-icaRttMS"; public const string constTableNameSessionDetailsIcaLatency = "citrix-session-details-icaLatency"; public const string constTableNameSessionDetailsClientL7Latency = "citrix-session-details-clientL7Latency"; @@ -378,10 +389,12 @@ namespace C4IT.DataHistoryProvider.Base.Modules.Citrix try { + Collector.DoProcessUiMessage(0, $"Validating Citrix connections..."); + List _toRemove = new List(); foreach (var _system in unauthorizedSystems) { - Collector.DoProcessUiMessage(2, $"Try logon to {_system.Name}..."); + Collector.DoProcessUiMessage(1, $"Try logon to {_system.Name}..."); var _ok = false; var CitrixConn = await LogonToSystemAsync(_system, token); if (token.IsCancellationRequested) @@ -396,9 +409,9 @@ namespace C4IT.DataHistoryProvider.Base.Modules.Citrix _ok = true; } if (_ok) - Collector.DoProcessUiMessage(2, $"Successfull logon to {_system.Name}."); + Collector.DoProcessUiMessage(1, $"Successfull logon to {_system.Name}."); else - Collector.DoProcessUiMessage(2, $"Could not logon to {_system.Name}.", LogLevels.Error); + Collector.DoProcessUiMessage(1, $"Could not logon to {_system.Name}.", LogLevels.Error); } foreach (var _system in _toRemove) @@ -413,34 +426,13 @@ namespace C4IT.DataHistoryProvider.Base.Modules.Citrix } finally { + Collector.DoProcessUiMessage(0, $"Validating Citrix connections finished."); if (CM != null) LogMethodEnd(CM); } return false; } - public async Task GetConnection(Guid SiteId) - { - MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } - - try - { - if (cachedConnections.TryGetValue(SiteId, out var cachedConnection)) - return cachedConnection; - - } - catch (Exception E) - { - LogException(E); - } - finally - { - if (CM != null) LogMethodEnd(CM); - } - - return null; - } - public async Task LogonCitrixTenantAsync(cDataHistoryCitrixCloudTenant Tenant, bool force = false) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } @@ -481,65 +473,6 @@ namespace C4IT.DataHistoryProvider.Base.Modules.Citrix return true; } - [Obsolete] - public async Task GetCitrixUsersAsync() - { - MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } - try - { - var strUrl = string.Format(constCitrixUserList, constCitrixUserPaging); - - var systems = Collector.InfrastructureConfig.Citrix.Systems; - if (systems is null) - { - Collector.DoProcessUiMessage(2, $" Could not logon to tenant - Tenant is null", LogLevels.Warning); - return false; - } - - var _retVal = true; - foreach (var _system in systems) - { - if (!(_system is cDataHistoryCitrixCloudTenant Tenant)) - continue; - - var CitrixConn = await GetCitrixCommunicationForSystemAsync(Tenant.TenantID, true); - if (CitrixConn == null) - { - Collector.DoProcessUiMessage(2, $"error: could not logon to tenat {Tenant.Domain}", LogLevels.Error); - _retVal = false; - continue; - } - - var Result = await CitrixConn.Monitor.Request.RequestAsync(strUrl, eHttpMethod.get, null); - if (Result != null) - { - var _props = Result.Result as JObject; - if (_props != null) - { - Collector.DoProcessUiMessage(3, $" properties of selected '{_props}' with {_props.Count} properties could be read."); - } - - } - else - { - Collector.DoProcessUiMessage(2, $" Could not get user list from tenant {Tenant.Domain}", LogLevels.Warning); - } - } - - return _retVal; - } - catch (Exception E) - { - LogException(E); - } - finally - { - if (CM != null) LogMethodEnd(CM); - } - - return false; - } - private class ScanSessionEntryProcessor : ScanEntryProcessor { private cCitrixCommunicationMonitor _comm; @@ -599,6 +532,7 @@ namespace C4IT.DataHistoryProvider.Base.Modules.Citrix LastUsed = endDate, UsingLevel = _duration == 0 ? 1 : _duration, id = SessionId, + Instance = instanceId, Status = enumF4sdSearchResultStatus.Active, Infos = Infos, Identities = new cF4sdIdentityList() @@ -679,90 +613,6 @@ namespace C4IT.DataHistoryProvider.Base.Modules.Citrix } return _retVal; - - string userIds = string.Join(",", UserIds.Where(u => !string.IsNullOrWhiteSpace(u?.citrixUserId.ToString())).Select(u => u.citrixUserId)); - - //DateTime latestEndDate = DateTime.UtcNow.AddDays(-age); - //ToDo: originale Altersberechnung wiederherstellen - DateTime latestEndDate = DateTime.UtcNow.AddDays(-90); - var strUrl = string.Format(constCitrixSessions, userIds, latestEndDate.ToString("yyyy-MM-ddThh:mm:ssK")); - - var tenantId = UserIds?.FirstOrDefault(u => !string.IsNullOrWhiteSpace(u?.citrixTenantId))?.citrixTenantId; - if (tenantId is null) - { - Collector.DoProcessUiMessage(2, $" Could not logon to tenant - Tenant is null", LogLevels.Warning); - return null; - } - - var CitrixConn = await GetCitrixCommunicationForSystemAsync(tenantId, true); - - if (CitrixConn == null) - { - Collector.DoProcessUiMessage(2, $"error: could not logon to tenat {tenantId}", LogLevels.Error); - return null; - } - - // ToDo: Implement a paged walkthrough of results in case there are more results than the API can return in one call - // maybe we should think about a concrete implementation of the results instead of a dynamic, what do you think? - var Result = await CitrixConn.Monitor.Request.RequestListAsync(strUrl, false, retryForbidden: false); - - if (Result != null && Result.Count > 0) - { - var sessionResult = new List(Result.Count); - double _sumDuration = 0; - foreach (var Entry in Result) - { - var endDate = DateTime.MinValue; - if (!DateTime.TryParse(Entry.Result.EndDate?.ToString(), out endDate)) - endDate = DateTime.UtcNow; - - var startDate = DateTime.MinValue; - if (!DateTime.TryParse(Entry.Result.StartDate?.ToString(), out startDate)) - startDate = DateTime.UtcNow; - - TimeSpan _duration = endDate - startDate; - _sumDuration += _duration.TotalSeconds; - - Enum.TryParse(Entry.Result.ConnectionState.ToString(), out enumCitrixSessionStatus connectionState); - - var Infos = new Dictionary() - { - - { "Status",connectionState.ToString() }, - { "StatusId", Entry.Result.ConnectionState.ToString() }, - { "Domain", Regex.Replace(Entry.Result.Machine.Name?.ToString(), @"\\.*$", "") } - }; - var sessionInfo = new cF4sdApiSearchResultRelation() - { - Type = enumF4sdSearchResultClass.VirtualSession, - Name = Regex.Replace(Entry.Result.Machine.Name.ToString(), @"^.*\\", ""), - DisplayName = Regex.Replace(Entry.Result.Machine.Name.ToString(), @"^.*\\", ""), - LastUsed = endDate, - // minor thing due to the fact we did it like that in the Agent before, but: - // Instead of setting the UsingLevel to a temporary value and calculate the actual value later, - // we should maybe calculate the sumvalue beforehand and apply only the calculation here, what do you think? - UsingLevel = _duration.TotalSeconds == 0 ? 1 : _duration.TotalSeconds, - id = Entry.Result.SessionKey, - Status = enumF4sdSearchResultStatus.Active, - Infos = Infos, - Identities = new cF4sdIdentityList() - { - new cF4sdIdentityEntry(){ Class = enumFasdInformationClass.User, Id = UserIds.First().Id}, - new cF4sdIdentityEntry(){ Class = enumFasdInformationClass.VirtualSession, Id = Entry.Result.SessionKey} - } - }; - sessionResult.Add(sessionInfo); - } - if (sessionResult != null && sessionResult.Count > 0) - { - foreach (var Entry in sessionResult) - { - Entry.UsingLevel /= _sumDuration; - } - } - return sessionResult; - } - } catch (Exception E) { @@ -919,179 +769,6 @@ namespace C4IT.DataHistoryProvider.Base.Modules.Citrix } } - private async Task> GetSessionStateMetricsAsync(cF4sdConnectorIds UserIds, string TenantId, cF4sdWebRequestInfo RequestInfo, int MaxAge, 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 - { - - var strUrl = string.Format(constCitrixSessions, UserIds.citrixUserId.ToString()); - - if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameSessionStateMetrics, out var SessionTableDetails)) - return null; - - var citrixCommunication = await GetCitrixCommunicationForSystemAsync(TenantId, true); - if (citrixCommunication == null) - return null; - - var res = await citrixCommunication.Monitor.Request.RequestAsync(strUrl, eHttpMethod.get, null); - if (res != null) - { - JObject sessionInfos = res.Result; - var retVal = new cF4SDHealthCardRawData.cHealthCardTable() - { - Name = constTableNameSessionStateMetrics, - InformationClass = enumFasdInformationClass.VirtualSession, - IsStatic = true, - TableType = eDataHistoryTableType.Static - }; - - var dicVals = new List>(); - var vals = sessionInfos.Children(); - var metricsObj = sessionInfos["value"] as JArray; - - if (metricsObj != null) - { - dicVals = metricsObj - .OfType() - .Where(o => - o["StartDate"] != null && - o["ConnectionState"] != null && - DateTime.TryParse(o["StartDate"].ToString(), out _) - ) - .GroupBy(o => new - { - Date = DateTime.Parse(o["StartDate"].ToString()).Date, - ConnectionState = (int)o["ConnectionState"] - }) - .OrderBy(g => g.Key.Date) - .Select(g => new Dictionary - { - { "StartDate", g.Key.Date }, // ✅ DateTime - { "ConnectionState", g.Key.ConnectionState }, - { "ConnectionCount", g.Count() } - }) - .ToList(); - - - - - const string startDateColumnName = "StartDate"; - const string connectionStatusColumnName = "ConnectionState"; - - var groupedValues = dicVals - .Where(v => - v.TryGetValue(startDateColumnName, out var collectDate) && - collectDate is DateTime && - v.TryGetValue(connectionStatusColumnName, out _)) - .GroupBy(v => new - { - StartDate = ((DateTime)v[startDateColumnName]).Date, - ConnectionStatus = v[connectionStatusColumnName] - }) - .Select(g => new Dictionary - { - { startDateColumnName, g.Key.StartDate }, - { connectionStatusColumnName, g.Key.ConnectionStatus }, - { "ConnectionCount", g.Count() } - }) - .ToLookup(d => new - { - StartDate = (DateTime)d[startDateColumnName], - ConnectionStatus = d[connectionStatusColumnName], - ConnectionCount = d["ConnectionCount"] - }); - - - - - for (int i = 0; i < MaxAge; i++) - { - DateTime referenceDate = DateTime.Now.AddDays(-i).Date; - - //var valuesOfDay = groupedValues.FirstOrDefault(v => v.Key.Date == referenceDate); - var valuesOfDay = groupedValues.Where(g => g.Key.StartDate == referenceDate).SelectMany(g => g).ToList(); - - foreach (var colInfo in SessionTableDetails.Columns.Values) - { - try - { - if (valuesOfDay is null) - { - AddColumnData(retVal, colInfo.SourceName, ConvertToF4sdType(null, colInfo.ValueType)); - continue; - } - - var valuesOfColumn = valuesOfDay - .Select(v => - { - if (v.TryGetValue(colInfo.SourceName, out var colValue)) - return colValue; - else - return null; - }) - .Where(v => v != null); - - if (!valuesOfColumn.Any()) - { - AddColumnData(retVal, colInfo.SourceName, ConvertToF4sdType(null, colInfo.ValueType)); - continue; - } - if (colInfo is cDataHistoryConfigColumn configColumn && configColumn.AggregationType != eDataHistoryAggregationType.Unknown) - { - var aggregatedValue = GetAggregationValue(valuesOfColumn, configColumn.AggregationType, colInfo.ValueType); - AddColumnData(retVal, colInfo.SourceName, ConvertToF4sdType(aggregatedValue, colInfo.ValueType)); - } - else - { - var value = valuesOfColumn.FirstOrDefault(); - AddColumnData(retVal, colInfo.SourceName, ConvertToF4sdType(value, colInfo.ValueType)); - } - - - - } - catch (Exception E) - { - LogException(E); - } - } - } - - return new List(1) { retVal }; - } - else - { - LogEntry($"Could not get detailed sessionmetrics information from citrix with id {UserIds}. Sessionmetrics content is empty.", 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); - } - - return null; - - void AddColumnData(cF4SDHealthCardRawData.cHealthCardTable table, string columnName, object value) - { - if (table.Columns.TryGetValue(columnName, out var columnValues)) - columnValues.Values.Add(value); - else - table.Columns.Add(columnName, new cF4SDHealthCardRawData.cHealthCardTableColumn(table) { ColumnName = columnName, Values = new List() { value } }); - } - } - private async Task> GetSessionMetricsDetailsIcaRttMSAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo, int MaxAge, DateTime RefTime, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } @@ -3038,6 +2715,127 @@ namespace C4IT.DataHistoryProvider.Base.Modules.Citrix } + private class RequestFieldList + { + internal Dictionary fields = new Dictionary(); + internal Dictionary SubNodes = new Dictionary(); + } + + private void addRequestField(RequestFieldList list, string fieldPath, string fieldName) + { + var arrItems = fieldPath.Split('.'); + if (arrItems.Length == 1) + list.fields[arrItems[0]] = fieldName; + else if (arrItems.Length > 1) + { + var _node = arrItems[0]; + var _newPath = fieldPath.Remove(0, _node.Length + 1); + if (!list.SubNodes.TryGetValue(_node, out var _newList)) + { + _newList = new RequestFieldList(); + list.SubNodes.Add(_node, _newList); + } + + addRequestField(_newList, _newPath, fieldName); + } + } + + private RequestFieldList getRequestFieldList(cDataHistoryConfigTable table) + { + try + { + var _result = new RequestFieldList(); + + foreach (var _colum in table.Columns.Values) + addRequestField(_result, _colum.SourceName, _colum.Name); + + return _result; + } + catch (Exception E) + { + LogException(E); + } + return null; + } + + private string getUrlSelectionSubFields(string name, RequestFieldList list) + { + var _select = string.Empty; + foreach (var _entry in list.fields) + { + if (_select != string.Empty) + _select += ","; + _select += _entry.Key; + } + + var _expand = string.Empty; + foreach (var _entry in list.SubNodes) + { + var _sub = getUrlSelectionSubFields(_entry.Key, _entry.Value); + if (!string.IsNullOrEmpty(_sub)) + { + if (_expand != string.Empty) + _expand += ","; + _expand += _sub; + } + } + + var _retVal = string.Empty; + if (_select != string.Empty) + _retVal = name + "($select=" + _select + ")"; + + if (_expand != string.Empty) + { + if (_retVal != string.Empty) + _retVal += ","; + _retVal += name + "($expand=" + _expand + ")"; + } + + return _retVal; + } + + private string getUrlSelection(RequestFieldList list) + { + try + { + var _select = string.Empty; + foreach (var _entry in list.fields) + { + if (_entry.Key == "SiteId" || _entry.Key == "SiteName") + continue; + if (_select != string.Empty) + _select += ","; + _select += _entry.Key; + } + + var _expand = string.Empty; + foreach (var _entry in list.SubNodes) + { + var _sub = getUrlSelectionSubFields(_entry.Key, _entry.Value); + if (!string.IsNullOrEmpty(_sub)) + { + if (_expand != string.Empty) + _expand += ","; + _expand += _sub; + } + } + + var _result = string.Empty; + if (_select != string.Empty) + _result = "&$select=" + _select; + + if (_expand != string.Empty) + _result += "&$expand=" + _expand; + + return _result; + } + catch (Exception E) + { + LogException(E); + } + return null; + } + public override async Task> GetTableResultsVirtualAsync(List Tables, Dictionary 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); } @@ -3049,118 +2847,78 @@ namespace C4IT.DataHistoryProvider.Base.Modules.Citrix if (!Identities.TryGetValue(enumFasdInformationClass.VirtualSession, out var session) || session?.Id is null) return null; - if (!Identities.TryGetValue(enumFasdInformationClass.User, out var userIds) || userIds?.citrixTenantId is null) + var _instance = session.Instance; + if (_instance == null) return null; - var citrixTableNames = new HashSet(Tables.Where(t => t?.ParentCluster?.Origin == enumDataHistoryOrigin.Citrix).Select(t => t.Name), StringComparer.Ordinal); - - bool Has(string tableName) => tableName != null && citrixTableNames.Contains(tableName); - - var _userExists = Has(constTableNameSessionUser); - var _sessionExists = Has(constTableNameSession); - var _machineExists = Has(constTableNameSessionMachine); - var _connectionExists = Has(constTableNameSessionConnection); - var _currentConnectionExists = Has(constTableNameSessionCurrentConnection); - var _sessionMetricsExists = Has(constTableNameSessionMetrics); - - var retVal = new List(); - var listTasks = new List>>(2); - - if (_sessionExists) - //if (CacheId == null && !instantly) - //{ - // retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable() - // { - // Name = constTableNameSession, - // InformationClass = enumFasdInformationClass.VirtualSession, - // IsIncomplete = true - // }); - - //} - //else - listTasks.Add(Task.Run>(async () => { return await GetSessionAsync(session.Id, userIds.citrixTenantId, requestInfo, LogDeep + 1, Token); })); - - if (_sessionMetricsExists) + var _detailTable = Tables.Where(_t => _t.Name == constTableNameSessionDetails).First(); + if (_detailTable == null) { - //if (CacheId == null && !instantly) - //{ - // retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable() - // { - // Name = constTableNameSessionMetrics, - // InformationClass = enumFasdInformationClass.VirtualSession, - // IsIncomplete = true - // }); + // ToDo: create an empty table + } + else + { + var _fieldList = getRequestFieldList(_detailTable); + var _select = getUrlSelection(_fieldList); + + var _url = string.Format(constCitrixSession, session.Id.ToString("D")) + _select; + + await ValidateConnectionsAsync(Token); + if (Token.IsCancellationRequested) return null; + + if (!cachedConnections.TryGetValue(_instance, out var _conn)) + return null; + + var _sessionData = await _conn.Monitor.Request.RequestAsync(_url, eHttpMethod.get, null, token: Token); + if (Token.IsCancellationRequested) return null; - //} - //else - listTasks.Add(Task.Run>(async () => { return await GetSessionMetricsAsync(session.Id, userIds.citrixTenantId, requestInfo, MaxAge, LogDeep + 1, Token); })); - //listTasks.Add(Task.Run>(async () => { return await GetSessionStateMetricsAsync(userIds, userIds.citrixTenantId, requestInfo, MaxAge, LogDeep + 1, Token); })); } - if (_machineExists) - //if (CacheId == null && !instantly) - //{ - // retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable() - // { - // Name = constTableNameSessionMachine, - // InformationClass = enumFasdInformationClass.VirtualSession, - // IsIncomplete = true - // }); - //} - //else - listTasks.Add(Task.Run>(async () => { return await GetSessionMachineAsync(session.Id, userIds.citrixTenantId, requestInfo, LogDeep + 1, Token); })); + return null; - if (_connectionExists) - //if (CacheId == null && !instantly) - //{ - // retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable() - // { - // Name = constTableNameSessionConnection, - // InformationClass = enumFasdInformationClass.VirtualSession, - // IsIncomplete = true - // }); - //} - //else - listTasks.Add(Task.Run>(async () => { return await GetSessionConnectionsAsync(session.Id, userIds.citrixTenantId, requestInfo, LogDeep + 1, Token); })); + //var citrixTableNames = new HashSet(Tables.Where(t => t?.ParentCluster?.Origin == enumDataHistoryOrigin.Citrix).Select(t => t.Name), StringComparer.Ordinal); + //bool Has(string tableName) => tableName != null && citrixTableNames.Contains(tableName); - if (_currentConnectionExists) - //if (CacheId == null && !instantly) - //{ - // retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable() - // { - // Name = constTableNameSessionCurrentConnection, - // InformationClass = enumFasdInformationClass.VirtualSession, - // IsIncomplete = true - // }); - //} - //else - listTasks.Add(Task.Run>(async () => { return await GetSessionCurrentConnectionAsync(session.Id, userIds.citrixTenantId, requestInfo, LogDeep + 1, Token); })); + //var _userExists = Has(constTableNameSessionUser); + //var _sessionExists = Has(constTableNameSession); + //var _machineExists = Has(constTableNameSessionMachine); + //var _connectionExists = Has(constTableNameSessionConnection); + //var _currentConnectionExists = Has(constTableNameSessionCurrentConnection); + //var _sessionMetricsExists = Has(constTableNameSessionMetrics); - if (_userExists) - //if (CacheId == null && !instantly) - //{ - // retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable() - // { - // Name = constTableNameSessionUser, - // InformationClass = enumFasdInformationClass.VirtualSession, - // IsIncomplete = true - // }); - //} - //else - listTasks.Add(Task.Run>(async () => { return await GetSessionUserAsync(session.Id, userIds.citrixTenantId, requestInfo, LogDeep + 1, Token); })); + //var retVal = new List(); + //var listTasks = new List>>(2); + + //if (_sessionExists) + // listTasks.Add(Task.Run>(async () => { return await GetSessionAsync(session.Id, userIds.citrixTenantId, requestInfo, LogDeep + 1, Token); })); + + //if (_sessionMetricsExists) + // listTasks.Add(Task.Run>(async () => { return await GetSessionMetricsAsync(session.Id, userIds.citrixTenantId, requestInfo, MaxAge, LogDeep + 1, Token); })); + + //if (_machineExists) + // listTasks.Add(Task.Run>(async () => { return await GetSessionMachineAsync(session.Id, userIds.citrixTenantId, requestInfo, LogDeep + 1, Token); })); + + //if (_connectionExists) + // listTasks.Add(Task.Run>(async () => { return await GetSessionConnectionsAsync(session.Id, userIds.citrixTenantId, requestInfo, LogDeep + 1, Token); })); + + //if (_currentConnectionExists) + // listTasks.Add(Task.Run>(async () => { return await GetSessionCurrentConnectionAsync(session.Id, userIds.citrixTenantId, requestInfo, LogDeep + 1, Token); })); + + //if (_userExists) + // listTasks.Add(Task.Run>(async () => { return await GetSessionUserAsync(session.Id, userIds.citrixTenantId, requestInfo, LogDeep + 1, Token); })); - var arrRes = await Task.WhenAll(listTasks.ToArray()); - foreach (var Entry in arrRes) - if (Entry != null) - retVal.AddRange(Entry); + //var arrRes = await Task.WhenAll(listTasks.ToArray()); + //foreach (var Entry in arrRes) + // if (Entry != null) + // retVal.AddRange(Entry); - if (retVal.Count == 0) - return null; + //if (retVal.Count == 0) + // return null; - return retVal; + //return retVal; } catch (Exception E) @@ -3176,56 +2934,6 @@ namespace C4IT.DataHistoryProvider.Base.Modules.Citrix return null; } - public async Task GetCitrixSessionsAsync() - { - MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } - try - { - var strUrl = string.Format(constCitrixSessionList, constCitrixUserPaging); - - var systems = Collector.InfrastructureConfig.Citrix.Systems; - if (systems is null) - { - Collector.DoProcessUiMessage(2, $" Could not logon to tenant - Tenant is null", LogLevels.Warning); - return false; - } - foreach (var _system in systems) - { - if (!(_system is cDataHistoryCitrixCloudTenant Tenant)) - continue; - - var CitrixConn = await GetCitrixCommunicationForSystemAsync(Tenant.TenantID, true); - if (CitrixConn == null) - { - Collector.DoProcessUiMessage(2, $"error: could not logon to tenat {Tenant.Domain}", LogLevels.Error); - continue; - } - - var Result = await CitrixConn.Monitor.Request.RequestAsync(strUrl, eHttpMethod.get, null); - if (Result != null) - { - var _ty = Result.GetType(); - var _props = Result.Result as JObject; - if (_props != null) - { - Collector.DoProcessUiMessage(3, $" properties of selected '{_props}' with {_props.Count} properties could be read."); - return true; - } - } - } - } - catch (Exception E) - { - LogException(E); - } - finally - { - if (CM != null) LogMethodEnd(CM); - } - - return false; - } - public async override Task 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); } diff --git a/F4SDwebService/Config/F4SD-DataClusters-Configuration.xml b/F4SDwebService/Config/F4SD-DataClusters-Configuration.xml index fee0b08..d6824a0 100644 --- a/F4SDwebService/Config/F4SD-DataClusters-Configuration.xml +++ b/F4SDwebService/Config/F4SD-DataClusters-Configuration.xml @@ -1338,6 +1338,79 @@
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ diff --git a/F4SDwebService/Config/F4SD-HealthCard-Configuration.xml b/F4SDwebService/Config/F4SD-HealthCard-Configuration.xml index 817c258..479942d 100644 --- a/F4SDwebService/Config/F4SD-HealthCard-Configuration.xml +++ b/F4SDwebService/Config/F4SD-HealthCard-Configuration.xml @@ -1530,7 +1530,7 @@ Benutzer-Informationen - + Benutzername diff --git a/F4SDwebService/Publish/Config/F4SD-DataClusters-Configuration.xml b/F4SDwebService/Publish/Config/F4SD-DataClusters-Configuration.xml index fee0b08..d6824a0 100644 --- a/F4SDwebService/Publish/Config/F4SD-DataClusters-Configuration.xml +++ b/F4SDwebService/Publish/Config/F4SD-DataClusters-Configuration.xml @@ -1338,6 +1338,79 @@
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ diff --git a/F4SDwebService/Publish/Config/F4SD-HealthCard-Configuration.xml b/F4SDwebService/Publish/Config/F4SD-HealthCard-Configuration.xml index 817c258..479942d 100644 --- a/F4SDwebService/Publish/Config/F4SD-HealthCard-Configuration.xml +++ b/F4SDwebService/Publish/Config/F4SD-HealthCard-Configuration.xml @@ -1530,7 +1530,7 @@ Benutzer-Informationen - + Benutzername