De App-V client kan de reporting data niet uploaden waardoor Event 3144 en error 0A-200001F4 gelogd worden. - Platform Support

Platform Support

De dagelijkse praktijk van Windows Platform Support in Nederland: jouw probleem, ons werk.

De App-V client kan de reporting data niet uploaden waardoor Event 3144 en error 0A-200001F4 gelogd worden.

Een tijdje geleden heb ik gewerkt aan een specifiek reporting probleem met App-V. Omdat dit issue eens in de zoveel tijd weer gemeld wordt, heb ik besloten dit in een blog samen te vatten, voor zowel onze Engelse als Nederlandse gebruikers.

Belangrijk: Voordat de acties en veranderingen in dit artikel worden toegepast moet er een backup gemaakt worden van de App-V SQL database!

Het probleem

Het probleem dat ik in dit artikel behandel is een reporting probleem binnen de App-V client. In dit geval kan de App-V client de reporting cache niet uploaden naar de App-V Management Server. In deze cache bewaren we onder andere de gebruiksinformatie van App-V applicaties, en wanneer dit probleem optreedt, kunnen er geen juiste rapporten worden gedraaid binnen App-V. Het is niet zo dat dit probleem op alle clients zal voorkomen, het kan ook zo zijn dat bepaalde clients wel de data uploaden, waardoor rapporten wel informatie bevatten. Wanneer een aantal clients het probleem wel heeft, zullen de rapporten onvolledig zijn.

De symptomen

Wanneer de App-V Management Server geconfigureerd is om reporting data van de clients te ontvangen, maar de Management Server wordt niet gebruikt om rapportages te draaien, zul je waarschijnlijk niet weten dat dit probleem zich voordoet. In dit geval is het ook een optie om de reporting data verzameling uit te zetten.

Over het algemeen hebben we de volgende symptomen voor dit probleem gezien:

• Waarschuwingen in de logs (Applicatie Event log en App-V client log).
• Foutmeldingen of onvolledige resultaten wanneer een rapport gedraaid wordt.
• Een toename van gebruikte schijfruimte op de App-V client omdat de XML bestanden niet worden opgeruimd.

In de App-V client log (sftlog.txt), kun je de volgende melding verwachten wanneer je dit issue ook ervaart:

[07/08/2011 07:56:28:947 REPT WRN] {tid=744:usr=USERNAME}
The Application Virtualization Client report data cache could not be sent to Publishing Server URL 'rtsp://APPVSERVER:554/' (rc = 1690640A-200001F4).

In de Applicatie Event Log zul je een bijbehorend event met ID 3144 vinden, maar het is belangrijk om niet alleen van Event 3144 uit te gaan in dit geval!

Event ID 3144 is een algemeen ID voor alle waarschuwingen binnen App-V met betrekking tot reporting. Het is daarom mogelijk dat de return code (RC) binnen dit event anders is, en de oorzaak van het probleem zal dan ook anders zijn. Wanneer je Event ID 3144 ziet, met een RC 0A-200001F4, en de symptomen hebt die eerder beschreven zijn, is het zeer waarschijnlijk dat je het probleem ervaart dat we in deze blog post bespreken.

Een belangrijk punt wat we in gedachten moeten houden voor dit probleem is dat de complete oplossing bestaat uit meerdere stappen, twee om precies te zijn. De oplossing voor het probleem zelf is een kleine stap, waarna de gegevens vanaf dat moment zonder problemen geupload zullen worden. Aangezien de gegevens van voor die tijd niet automatisch geupload zullen worden, moeten we daar specifieke stappen voor ondernemen, wanneer deze zogenaamde “history data” wel gewenst is. Wanneer deze informatie nodig is, zullen we deze aan de hand van een vrij complexe procedure in de App-V SQL database moeten importeren.

Achtergrondinformatie over App-V Reporting

Toen we App-V versie 4.5 uitbrachten, hebben we de manier waarop we de rapportage in zijn algemeenheid werkt veranderd. Sinds App-V 4.5 gebruiken we XML bestanden op de App-V client waarin we de informatie tijdelijk opslaan tussen de Desktop Configuration Refreshes (DC Refresh). Wanneer een DC Refresh uitgevoerd wordt, sturen we de informatie uit deze XML naar de App-V Management Server.

Vanwege de veranderingen en de verschillen tussen SoftGrid 4.1.x.x en App-V 4.5 is het niet mogelijk om de informatie van een App-V 4.5+ client naar een SoftGrid 4.1.x.x server te uploaden. Wanneer dit het geval is, zullen we een onderstaande melding zien in onze App-V client log (sftlog.txt):

[08/24/2010 14:55:18:179 MIME VRB] {tid=BA4:usr=Administrator}
DC server does not want to receive v4.5+ reporting

Tevens zullen we zien dat onderstaande registersleutel op 0 staat wanneer een App-V 4.5+ client naar een SoftGrid 4.1.x.x server verbindt. Wanneer we naar een App-V 4.5 Management Server verbinden, staat deze sleutel op 1.

Op x86:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\SoftGrid\4.5\Client\DC Servers\Reporting

Op x64:
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\SoftGrid\4.5\Client\DC Servers\Reporting

Wanneer de bovenstaande “reporting” sleutel op 1 staat, zul je ook de volgende sleutels hebben in het register. Deze sleutel bevat de naam en de locatie van het meest recente XML bestand.

Op x86:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\SoftGrid\4.5\Client\Reporting\LastCacheFile

Op x64:
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\SoftGrid\4.5\Client\Reporting\LastCacheFile

Dit XML bestand is een fysiek bestand op de App-V client en het bevat alle gebruiksinformatie voor de App-V applicaties op de desbetreffende client. Deze info, samen met wat algemene andere info, staat in XML formaat in dit bestand. Wanneer we een DC refresh uitvoeren, wordt deze XML verwerkt en in SQL geimporteerd. Wanneer dit gelukt is, zal het lokale bestand op de client worden geleegd. Een nieuwe XML zal dan worden aangemaakt zodat de nieuwe info bewaard kan worden tot een volgende DC Refresh. Op deze manier kan de informatie gebruikt worden in onze rapportages.

Belangrijk: De standaardlocatie voor het XML bestand is de Global Data Directory. Om het bestand daadwerkelijk te kunnen zien zul je verborgen bestanden en mappen moeten weergeven!

Hieronder staat een voorbeeld van hoe ons XML bestand eruit ziet wanneer we een App-V applicatie hebben gestart op die client en voordat het geupload wordt. Wanneer we nu een DC refresh zouden uitvoeren, wordt dit verwerkt, geupload en in SQL geimporteerd.

Nadat een DC Refresh is uitgevoerd, zullen we zien dat het XML bestand leeg is, totdat we weer nieuwe applicaties starten:

Tevens kunnen we onze App-V NTA logging gebruiken om te zien dat wanneer een DC refresh wordt gedaan, we het verzoek krijgen om de reporting informatie te sturen:

[10/31/2011 14:54:12.344, 0x03144] SWRTSPRequest::CreateRequest: Method(SET_PARAMETER), Url(rtsp://madelw-vas:554/), Header(Content-Type: text/xml
AppV-Op: Report
)

In een verbose NTA log kunnen we precies zien wat er over de draad gaat. Wanneer we de reporting informatie uploaden, wordt de lokale XML geparsed en naar de App-V Management Server toegestuurd.  Dit kunnen we exact zien in het frame welke de “CLIENT_DATA” sectie bevat. Dit deel bevat alle info van het XML bestand.

Wanneer we onze NTA log zouden strippen in een situatie waar alles naar behoren werkt, zouden we het volgende kunnen zien:

<PKG_DATA.Name="{2920C30C-FB29-4E28-842D-34934585C7E0}".Guid="{2920C30C-FB29-4E28-842D-34934585C7E0}".Ver="0".VerGuid="{00000000-0000-0000-0000-000000000000}".Source="RTSP://%SFT_SOFTGRIDSERVER%:554/APPNAME.sft".PctCached="0"/>"

Wanneer het niet naar behoren werkt, en we ervaren ons probleem, dan zouden we het volgende zien:

<PKG_DATA.Name="".Guid="{2920C30C-FB29-4E28-842D-34934585C7E0}".Ver="0".VerGuid="{00000000-0000-0000-0000-000000000000}".Source="RTSP://%SFT_SOFTGRIDSERVER%:554/APPNAME.sft".PctCached="0"/>"

Zoals je in de bovenstaande voorbeelden kunt zien, is de PKG_DATA.Name leeg in de kapotte situatie. En dit is precies de reden dat we dit probleem ervaren!

De oplossing

Met de bovenstaande informatie in onze gedachten gehouden, kunnen we zien dat de reden voor dit probleem het feit is dat de PKG_DATA.Name geen waarde heeft. Zoals eerder aangegeven, wordt alle XML informatie in de SQL database geimporteerd. In dit geval is de kolom binnen SQL waar onze PKG_DATA.Name in moet komen zo ingesteld dat deze geen NULL waarden accepteert.

Dit kunnen we gemakkelijk controleren in de SQL Management Studio, door de kolomeigenschappen van de package_name kolom te openen binnen de dbo.REPORTING_PACKAGE_INFORMATION tabel.

Allow Nulls is hier op False gezet, zoals we hieronder kunnen zien:

Dit is te veranderen door het onderstaande SQL statement uit te voeren in de SQL Management Studio. Wanneer dit gedaan is zullen we zien dat we vanaf dat moment wel NULL waarden aankunnen binnen deze kolom.

Wel moeten we ervoor zorgen dat we de juiste database selecteren wanneer we deze code uitvoeren, anders zal dit mislukken!

alter table REPORTING_PACKAGE_INFORMATION
alter column package_name nvarchar(256) null

Wanneer het uitvoeren van dit SQL statement goed is gegaan, zal vanaf dat moment het probleem opgelost zijn, en vanaf dat moment zal alle reporting data netjes in SQL geïmporteerd kunnen worden.

Hoe kunnen we de achtergebleven informatie importeren?

Zoals al eerder aangegeven, is het niet klaar met alleen het oplossen van het probleem. Ondanks dat we nu alle reporting informatie netjes in SQL zetten, kan het zo zijn dat de oude data die achter is gebleven op de clients nodig is in de rapporten. Het is mogelijk om deze achtergebleven informatie achteraf te importeren, al is dit wel een vrij complexe procedure.

Om dit te doen, moeten we een speciale SQL Stored Procedure gebruiken om de achtergebleven info in SQL te zetten aan de hand van een bulk import actie. Hieronder staat de Stored Procedure in tekst formaat.

Belangrijk: Deze Stored Procedure is getest op SQL 2008. Wanneer deze gebruikt gaat worden op andere versies, kan het zijn dat er aanpassingen nodig zijn. Test deze dus goed voor gebruik!

Deze Stored Procedure moet worden opgeslagen in een bestand genaamd:


“proc_ImportReportingCacheFilesv3.sql”

SET NOCOUNT ON
if exists(
 select 1 from sysobjects where id = object_id('proc_ImportReportingCacheFilev3')
 and OBJECTPROPERTY(id, N'IsProcedure') = 1
)
begin
 drop procedure proc_ImportReportingCacheFilev3
end

go

                                 

create procedure proc_ImportReportingCacheFilev3
(
 @client_host_name nvarchar(256),
 @cache_file_name nvarchar(256)
)
as

if @client_host_name is null
begin
 raiserror (N'Client host name cannot be NULL', 16, 1)
 return
end

if @cache_file_name is null
begin
 raiserror (N'Reporting cache file name cannot be NULL', 16, 1)
 return
end

-- First, ensure that there is a row in the table that tracks basic
-- metadata for all reporting clients. If there isn't one, we will
-- synthesize a placeholder row with just enough information to allow
-- the app usage data to import correctly

if not exists ( select 1 from REPORTING_CLIENT_INFORMATION where host_name = @client_host_name )
begin
 insert into REPORTING_CLIENT_INFORMATION
  ( host_name, version, cache_size, cache_used )
 values
  ( @client_host_name, N'4.6.0.1523', 1, 0 )
end

declare @hostid uniqueidentifier

select
 @hostid = host_id
from
 REPORTING_CLIENT_INFORMATION
where
 host_name = @client_host_name
 
print N'Mapped host_id for client ' + @client_host_name + N' as ' + rtrim(cast(@hostid as nvarchar(50)))

-- Load the reporting cache document
declare @doc xml
declare @sql nvarchar(1024)

set @sql = N'select @doc_out = ( select * from openrowset ( bulk ''' +
 @cache_file_name + N''', single_blob ) as data )'

execute sp_executesql @sql, N'@doc_out xml output', @doc_out = @doc output

if @@ERROR = 0
begin


if NOT exists(
 select 1 from sysobjects where id = object_id('imported_records')
 
)
begin
  create table imported_records
 (
  row_id      int      not null identity,
  start_time  datetime not null,
  end_time    datetime null,
  app_name    nvarchar(64) not null,
  app_version nvarchar(16) not null,
  username    nvarchar(256) not null,
  server_name nvarchar(256) not null
 )
Print 'Table Created for 1st Iteration: imported_records' 
end

Print 'Table : imported_records Exists' 
 
 insert into imported_records
  ( start_time, end_time, app_name, app_version, username, server_name )
 select
  cache.start_time,
  case when cache.end_time = '' then null else cache.end_time end,
  cache.app_name, cache.app_version,
  cache.username, cache.server_name
 from
  ( select 
   parsed.content.query('data(@Launched)').value('.', 'datetime') as start_time,
   parsed.content.query('data(@Shutdown)').value('.', 'datetime') as end_time,
   parsed.content.query('data(@Name)').value('.', 'nvarchar(64)') as app_name,
   parsed.content.query('data(@Ver)').value('.', 'nvarchar(16)') as app_version,
   parsed.content.query('data(@User)').value('.', 'nvarchar(256)') as username,
   parsed.content.query('data(@Server)').value('.', 'nvarchar(256)') as server_name
    from
   @doc.nodes('/REPORT_DATA_CACHE/APP_RECORDS/APP_RECORD') as parsed(content)
  ) as cache
  
 print N'Loaded ' + rtrim(cast(@@rowcount as nvarchar(30))) + N' records from file ' + @cache_file_name

 -- Prune any records that look like they have already been loaded
 delete from imported_records
 where exists
 ( select
  1
   from
  APPLICATION_USAGE
  where
  imported_records.start_time = APPLICATION_USAGE.start_time and
  imported_records.app_name = APPLICATION_USAGE.app_name and
  imported_records.app_version = APPLICATION_USAGE.app_version and
  imported_records.username = APPLICATION_USAGE.username and
  APPLICATION_USAGE.host_id = @hostid
 )
 
 print N'Removed ' + rtrim(cast(@@rowcount as nvarchar(30))) + N' records that look to have been already uploaded.'
 
 -- Prune duplicate records in the import list. Since they're duplicates, it
 -- doesn't really matter which one we keep, so keep the one with the smallest
 -- row_id
 
 delete from imported_records
 where exists
 ( select
  1
   from
  imported_records as imported_records_inner
   where
  imported_records_inner.start_time = imported_records.start_time and
  imported_records_inner.app_name = imported_records.app_name and
  imported_records_inner.app_version = imported_records.app_version and
  imported_records_inner.username = imported_records.username
   group by
     start_time, app_name, app_version, username
   having
     count(0) > 1 and min(imported_records_inner.row_id) != imported_records.row_id
 )
 
 print N'Removed ' + rtrim(cast(@@rowcount as nvarchar(30))) + N' records that are duplicates within the file itself.'
    
 -- Prune records in the import list that indicate that an app launch ended before
 -- it started.
 
 delete from imported_records where end_time is not null and start_time > end_time
 
 print N'Removed ' + rtrim(cast(@@rowcount as nvarchar(30))) + N' records with an incongruous end_time.'
    
 -- Now the new records can be fed to the app usage table
 insert into APPLICATION_USAGE
 ( start_time, end_time, shutdown_state, app_name, app_version,
   app_id, username, server_name, host_id )
 select
  imported_records.start_time, imported_records.end_time,
  case when imported_records.end_time is null or imported_records.end_time = '' then 0 else 1 end,
  imported_records.app_name, imported_records.app_version,
  APPLICATIONS.app_id,
  imported_records.username, imported_records.server_name,
  @hostid
  from
  imported_records,
  APPLICATIONS
  where
  APPLICATIONS.name = imported_records.app_name and
  APPLICATIONS.version = imported_records.app_version
 
  print N'Added the remaining ' + rtrim(cast(@@rowcount as nvarchar(30))) + N' records with matching applications to the APPLICATION_USAGE table.' 
 
 
  print N'Cleaning up... : imported_records'
  truncate table imported_records  
  print N'Table : imported_records truncated'
 
end

go

Om deze Stored Procedure aan de betreffende SQL server toe te voegen, kunnen de volgende stappen gevolgd worden:

• Open de SQL Server Management Studio.
• Selecteer de App-V database.
• Open een nieuw query schern en kopieer de Stored Procedure hier.
• Voer deze uit (F5).
• Je kunt nu zien dat de SP toegevoegd is aan SQL, en deze kunnen we later gebruiken:

Wanneer we een groot aantal achtergebleven XML bestanden hebben verspreid over onze clients, dan zullen we deze eerst moeten verzamelen en in een bepaalde map moeten opslaan. Wanneer alle XML bestanden in een map zijn verzameld, kunnen we onderstaande code gebruiken om de benodigde T-SQL code te maken welke we gaan gebruiken om de bulk import te doen.

Met deze T-SQL output starten we de bulk import automatisch en alle XML informatie uit alle files zal geimporteerd worden.

for /d %d in (C:\location where the orphaned XML files are gathered\*) do @for /f %f in ('dir /b /a:h %d\*.xml') do @echo exec proc_ImportReportingCacheFilev3 N'%~nd.fulldomainname', N'%~fd\%~f' >>c:\output.sql

Wanneer we de bestanden hebben verzameld, zoals in onderstaand voorbeeld, zorg er dan voor dat je de subfolder naam, in dit geval “Clientname001” NIET in het commando opneemt.

De /d optie in de FOR loop in de code zorgt er al voor dat de code door alle subfolders zal gaan. Wanneer we dus een map hebben genaamd “C:\Test”, met alle submappen daarin voor elke client, hoeven we alleen de “C:\Test” aan te geven. Een voorbeeld hiervan is hieronder weergegeven:

Voorbeeld:

for /d %d in (C:\test\*) do @for /f %f in ('dir /b /a:h %d\*.xml') do @echo exec proc_ImportReportingCacheFilev3 N'%~nd.domain.com', N'%~fd\%~f' >>c:\output.sql

Belangrijk: Het kan een aantal minuten duren voordat deze code klaar is, afhankelijk van de hoeveelheid bestanden en mappen!

Wanneer het commando goed werkt, zullen we een output bestand krijgen als resultaat. Dit bestand zal eruit zien zoals onderstaand voorbeeld. Het bestand zal 1 regel code per XML bestand hebben.

 
Belangrijk: We moeten controleren dat de output evenveel regels tekst bevat als de totale hoeveelheid XML bestanden voordat we de T-SQL code uit gaan voeren!
Nu we deze output hebben, kunnen we deze in de SQL Management Studio uitvoeren. Tijdens het uitvoeren zal alle XML info in de database gezet worden.

Wanneer we een groot aantal bestanden hebben om te importeren (honderden of misschien zelfs duizenden), moeten we er rekening mee houden dat dit vrij intensief kan zijn op de SQL database server. Daarom raden we aan om deze actie buiten de kantooruren uit te voeren.

Het is ook mogelijk om de code in stappen uit te voeren en zo een aantal bestanden per keer in te voeren, om zo eventuele problemen in een vroeg stadium te ontdekken.

Wanneer we de T-SQL uitvoeren, zullen we een melding krijgen wanneer de code is uitgevoerd, zoals hieronder weergegeven. Nu kunnen we de geimporteerde data gaan controleren door App-V rapportages te draaien. Deze zouden nu de juiste en complete informatie moeten bevatten.

Wanneer we de resultaten hebben gecontroleerd op juistheid, is er nog een laatste ding wat we moeten doen, namelijk de tijdelijke tabel die SQL heeft gebruikt opruimen.

Dit doen we door onderstaande statement uit te voeren:

drop table imported_records

Nu alles weer in orde is, raadt ik aan om een nieuwe backup te maken van de database, zodat deze hele procedure niet nogmaals hoeft uitgevoerd te worden.

Hopelijk kunnen hiermee alle “0A-200001F4” problemen worden opgelost!

Madelinde Walraven, Support Escalation Engineer

Commentaar:

Geen commentaar

Wat denkt u?

(Verplicht) 

(Verplicht) 

(Optioneel)

(Verplicht) 
CaptchaCube Vraag:


Antwoord: