Kace: un alert in caso di client mancante o inventario troppo vecchio

| |

In seguito a una migrazione della virtual appliance di Kace, alcuni dei client dislocati sul territorio hanno perso “la retta via di casa“, non comunicando più correttamente con la console del software di Quest. Per questo motivo, dato che tutti eseguivano almeno uno script di logon, ho deciso di inserire un richiamo a un VBScript che ho sviluppato (prendendo spunto da moltissime fonti, riportate nei credits dello script, nda) e che si occupa di verificare che l’agente Kace sia installato (fondamentale) ein caso lo fosseche l’inventario ultimo non sia più vecchio di 10 giorni.

Kace: un alert in caso di client mancante o inventario troppo vecchio

A oggi i risultati sono stati molto positivi, abbiamo visto spuntare fuori diverse macchine sulle quali è poi bastato connettersi via prompt (niente che non possa essere eseguito da un PsExec) per mettere a posto le cose (te lo spiego più tardi).

La logica di funzionamento

Il principio dello script è semplice e si basa su un comportamento attuale dell’agente di Kace. Se questo è installato, vuol dire che tra i servizi attivi della macchina dovrà esserci Konea, nome assegnato già da Dell prima che Quest ereditasse il prodotto. Se hai l’agente Kace installato sulla tua macchina e vuoi fare un banale controllo, prova a lanciare un sc query Konea via prompt dei comandi, dovresti ottenere un risultato simile:

NOME_SERVIZIO: konea
 TIPO : 10 WIN32_OWN_PROCESS
 STATO : 4 RUNNING
 (STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN)
 CODICE_USCITA_WIN32 : 0 (0x0)
 CODICE_USCITA_SERVIZIO : 0 (0x0)
 PUNTO_CONTROLLO : 0x0
 INDICAZIONE_ATTESA : 0x0

Detto ciò, si passa alla seconda verifica: un inventario sufficientemente aggiornato anche per coloro che magari non accendono il PC per qualche giorno (penso alle ferie nei periodi estivi o invernali, o magari a breve trasferte lavorative anche offline). Ho pensato quindi che 10 giorni fosse un corretto intervallo di tempo massimo, oltre il quale devo allarmare qualcuno affinché faccia un controllo sulla console.

Dato che in alcuni casi è necessario ignorare determinate macchine sulle quali ci si autentica con le proprie credenziali, lo script prevede anche delle esclusioni in base al nome macchina,ciò implica però che tu vada a utilizzare una nomenclatura ben precisa (altrimenti ti toccherà inserire una miriade di nomi diversi).

Lo script

Ho notato che l’agente di Kace aggiorna il file kinventory.db ogni volta che comunica il suo inventario completo. Per questo motivo, lo prendo come punto di riferimento e mi accerto che l’ultima data di modifica del file non sia più vecchia dell’intervallo stabilito (intervallo che tu, secondo una diversa esigenza o ragionamento, puoi chiaramente modificare).

Se anche questo controllo passa indenne, nessun problema. In caso contrario, spedirò una mail a un singolo destinatario o un gruppo di HelpDesk che possa far partire i controlli necessari.

Tradotto in codice (anche non elegante, non ottimizzato e chissà cos’altro ancora), questo è il risultato:


' Verifica esistenza servizio Dell Kace (KONEA) e ultimo inventario
' ———————————————————————————————————–
' Author: GSolone
' Version: 0.2 rev1
' Info: https://gioxx.org/tag/VBScript
' Last modified: 16-10-2017
' Credits: https://www.symantec.com/connect/downloads/vbscript-delete-old-files
' http://www.robvanderwoude.com/vbstech_network_ip.php
' http://www.robvanderwoude.com/vbstech_network_names_computer.php
' https://social.technet.microsoft.com/Forums/en-US/a8d29535-217e-46c0-bd94-0a203a322015/current-logged-in-user-using-vbscript?forum=ITCG
' https://gallery.technet.microsoft.com/scriptcenter/01fcf945-ad73-44e0-8cb5-152432bc6bcf
' https://rcmtech.wordpress.com/2011/04/15/vbscript-instr-with-the-and-operator/
' https://stackoverflow.com/questions/11879612/qtp-checking-if-an-array-of-strings-contains-a-value
' ————————————————————————————————————
Dim oFSO, oFolder, sDirectoryPath
Dim oFileCollection, oFile, sDir
Dim iDaysOld
sDirectoryPath = "C:\ProgramData\Dell\Kace"
Set objNTInfo = CreateObject("WinNTSystemInfo")
Set objMessage = CreateObject("CDO.Message")
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set wshShell = CreateObject("WScript.Shell")
Set oFolder = oFSO.GetFolder(sDirectoryPath)
Set oFileCollection = oFolder.Files
Set objNetwork = CreateObject("Wscript.Network")
Set objSysInfo = Createobject("ADSystemInfo")
iDaysOld = 10
strComputerName = wshShell.ExpandEnvironmentStrings("%COMPUTERNAME%")
' ===== QUIT DALLO SCRIPT SE LO SCRIPT VIENE AVVIATO SU DETERMINATE MACCHINE ========================================================
Dim exclusions
exclusions = Array( "WIN7TEST", _
"WIN10TEST")
For j=Lbound(exclusions) to Ubound(exclusions)
If InStr(strComputerName, exclusions(j))> 0 Then
'MsgBox("Debug: found " & strComputerName & " at index " & j)
WScript.Quit
End If
Next
' ===== VERIFICA ESISTENZA SERVIZIO KONEA, QUINDI VERIFICA ULTIMO INVENTARIO ========================================================
strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colRunningServices = objWMIService.ExecQuery("Select * from Win32_Service Where Name='konea'")
nItems = colRunningServices.Count
' If the collection count is greater than zero the service will exist.
If nItems > 0 Then
For each oFile in oFileCollection
'DEBUG: il file cacert.pem è sempre più vecchio dei 10 giorni (su macchine già avviate da tempo), si può utilizzare per verificare che lo script lo rilevi e invii alert mail:
'If oFile.Name = "cacert.pem" Then
If oFile.Name = "kinventory.db" Then
If oFile.DateLastModified < (Date() – iDaysOld) Then
Destination = "tuonome@contoso.com"
DestinationCC = ""
DestinationBCC = ""
MailBody = "KACE Agent <strong>non connesso o non aggiornato</strong>, verificare quanto prima da console.<br /><br />" &_
"<li>Workstation: <strong>" & strComputerName & "</strong></li>"
strQuery = "SELECT * FROM Win32_NetworkAdapterConfiguration WHERE MACAddress > ''"
Set objWMIService = GetObject( "winmgmts://./root/CIMV2" )
Set colItems = objWMIService.ExecQuery( strQuery, "WQL", 48 )
For Each objItem In colItems
If IsArray(objItem.IPAddress) Then
If UBound(objItem.IPAddress) = 0 Then
strIP = "<li>IP macchina: <strong>" & objItem.IPAddress(0) & "</strong></li>"
Else
strIP = "<li>IP macchina: <strong>" & Join( objItem.IPAddress, ", " ) & "</strong></li>"
End If
End If
Next
MailBody = MailBody & strIP &_
"<li>Utente connesso (se rilevato): <strong>" & objNetwork.UserName & "</strong></li>" &_
"<li>DN utente connesso (se rilevato): <strong>" & objSysInfo.UserName & "</strong></li>"
objMessage.Subject = "KACE Agent non aggiornato o non connesso su " & strComputerName
objMessage.From = "Alert Kace <kacealert@contoso.com>"
objMessage.To = Destination
objMessage.Cc = DestinationCC
objMessage.Bcc = DestinationBCC
objMessage.HTMLBody = MailBody
objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "smtp.contoso.com"
objMessage.Configuration.Fields.Update
objMessage.Send
End If
End If
Next
Set oFSO = Nothing
Set oFolder = Nothing
Set oFileCollection = Nothing
Set oFile = Nothing
Else
Destination = "tuonome@contoso.com"
DestinationCC = ""
DestinationBCC = ""
MailBody = "KACE Agent <strong>non installato</strong>, installare quanto prima.<br /><br />" &_
"<li>Workstation: <strong>" & strComputerName & "</strong></li>"
strQuery = "SELECT * FROM Win32_NetworkAdapterConfiguration WHERE MACAddress > ''"
Set objWMIService = GetObject( "winmgmts://./root/CIMV2" )
Set colItems = objWMIService.ExecQuery( strQuery, "WQL", 48 )
For Each objItem In colItems
If IsArray(objItem.IPAddress) Then
If UBound(objItem.IPAddress) = 0 Then
strIP = "<li>IP macchina: <strong>" & objItem.IPAddress(0) & "</strong></li>"
Else
strIP = "<li>IP macchina: <strong>" & Join( objItem.IPAddress, ", " ) & "</strong></li>"
End If
End If
Next
MailBody = MailBody & strIP &_
"<li>Utente connesso (se rilevato): <strong>" & objNetwork.UserName & "</strong></li>" &_
"<li>DN utente connesso (se rilevato): <strong>" & objSysInfo.UserName & "</strong></li>"
objMessage.Subject = "KACE Agent non installato " & strComputerName
objMessage.From = "Alert Kace <kacealert@contoso.com>"
objMessage.To = Destination
objMessage.Cc = DestinationCC
objMessage.Bcc = DestinationBCC
objMessage.HTMLBody = MailBody
objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "smtp.contoso.com"
objMessage.Configuration.Fields.Update
objMessage.Send
End If

view raw

NotifyKace.vbs

hosted with ❤ by GitHub

Cosa c’è da sapere, cosa dovrai modificare

I punti modificabili all’interno dello script sono diversi, alcuni necessari, altri meno (nel senso che puoi lasciarli invariati rispetto al mio suggerimento), andiamo con ordine.

  • sDirectoryPath = "C:\ProgramData\Dell\Kace": salvo modifiche da parte di Quest, la cartella dove risiede il DB di inventario è questa, e vale per Windows 7 o superiore. Se ci saranno modifiche in futuro, aggiornerò lo script e l’articolo, puoi starne certo.
  • iDaysOld = 10: modificando quel 10, modificherai i giorni oltre i quali allarmarsi per un possibile disallineamento dell’agente Kace rispetto alla console. Credo che il mio sia un giusto intervallo, ma ogni realtà è diversa, così come ogni esigenza.
  • exclusions = Array( "WIN7TEST", _: per ogni riga, potrai specificare una parte di nome che –nel caso in cui dovesse far parte dell’hostname– verrà ignorata, facendo chiudere in automatico lo script. Nello script di esempio pubblicato su Gist (quello che vedi poco sopra), ho pensato a macchine virtuali (o fisiche) che contengano nel loro nome WIN7TEST o WIN10TEST. Cambia quei nomi con ciò che ti interessa ignorare, altrimenti non curarti di questa porzione di codice e vai avanti.
  • Destination = "tuonome@contoso.com": ogni campo Destination (ce ne sono due, nda) dovrà contenere l’indirizzo di posta di chi riceverà la mail di alert in caso di problemi. Sostituisci quindi “tuonome@contoso.com” con un indirizzo di posta esistente e sensato.
  • objMessage.From = "Alert Kace <kacealert@contoso.com>: un po’ come sopra, anche se questo è in realtà l’indirizzo del mittente che invierà la mail. Può essere anche fittizio, ma occhio ai controlli antispam del tuo client di posta.
  • "smtp.contoso.com": sostituiscilo con l’indirizzo del tuo server SMTP, possibilmente interno alla tua rete.
Kace: un alert in caso di client mancante o inventario troppo vecchio 1ATTENZIONE: Nel caso in cui tu non avessi un SMTP interno alla tua rete aziendale, dovrai passare per un provider esterno. In questo caso, potresti dover utilizzare un’autenticazione per spedire posta. Dai un’occhiata alla pagina paulsadowski.com/wsh/cdo.htm per sapere come modificare lo script e procedere (oppure chiedi aiuto nell’area commenti del mio blog).

Fai molta attenzione. Come anticipato, ci sono due campi per Destination, objMessage.From e smtp.contoso.com. Lo script, salvo errori, ti consegnerà comodamente in Inbox delle mail di questo tipo:

Tocca ora a te collegarti alle macchine tramite prompt dei comandi e procedere con le opportune operazioni. Ti riepilogo brevemente in cosa potresti imbatterti.

Kace: amptools

Spostati nella cartella di installazione del Kace (C:\Program Files (x86)\Dell\KACE per i sistemi a 64 bit, C:\Program Files\Dell\KACE per quelli a 32) e procedi verificando la configurazione del tuo agente Kace. Questa è memorizzata nel file amp.conf che si trova nella %ProgramData%, ma puoi facilmente ottenerla lanciando il comando amptools get:

C:\Program Files (x86)\Dell\KACE>amptools get
[2017-11-06.09:05:23][AMPTools:AgentConfig::LogAll ] Agent Configuration Values
[2017-11-06.09:05:23][AMPTools:AgentConfig::LogAll ] KACE Agent version 7.2.10 Jun 5 2017
[2017-11-06.09:05:23][AMPTools:AgentConfig::LogAll ] ------------------------------------------------
[2017-11-06.09:05:24][AMPTools:AgentConfig::LogAll ] debug: true
[2017-11-06.09:05:24][AMPTools:AgentConfig::LogAll ] host: kace.contoso.com
[2017-11-06.09:05:24][AMPTools:AgentConfig::LogAll ] weburl: https://kace.contoso.com
[2017-11-06.09:05:24][AMPTools:AgentConfig::LogAll ] webport: 443
[2017-11-06.09:05:24][AMPTools:AgentConfig::LogAll ] serverversion: 7.2.101
[2017-11-06.09:05:24][AMPTools:AgentConfig::LogAll ] max download speed: unlimited
[2017-11-06.09:05:24][AMPTools:AgentConfig::LogAll ] companyname: Contoso Corp.
[2017-11-06.09:05:24][AMPTools:AgentConfig::LogAll ] splashtext: KACE Systems Management Appliance is verifying your system configuration and managing software updates. Please Wait...
[2017-11-06.09:05:24][AMPTools:AgentConfig::LogAll ] processtimeout: 14400000
[2017-11-06.09:05:24][AMPTools:AgentConfig::LogAll ] filetimeout: 86400
[2017-11-06.09:05:24][AMPTools:AgentConfig::LogAll ] sslrequired: TRUE
[2017-11-06.09:05:24][AMPTools:AgentConfig::LogAll ] verifyssl: FALSE
[2017-11-06.09:05:24][AMPTools:AgentConfig::LogAll ] koneaport: 443
[2017-11-06.09:05:24][AMPTools:AgentConfig::LogAll ] servercompress: TRUE
[2017-11-06.09:05:24][AMPTools:AgentConfig::LogAll ] disablebootupsplash: FALSE
[2017-11-06.09:05:24][AMPTools:AgentConfig::LogAll ] disableloginsplash: FALSE
[2017-11-06.09:05:24][AMPTools:AgentConfig::LogAll ] disablewaitforbootuptasks: FALSE
[2017-11-06.09:05:24][AMPTools:AgentConfig::LogAll ] disablewaitforlogintasks: FALSE

Assicurati che i campi host e weburl siano corretti e, nel caso non lo fossero, procedi secondo un ragionamento semplice:

  • se la versione dell’agente è antecedente la 7, dovrai disinstallarlo, quindi installare la versione più recente, questo a causa di una incompatibilità con console esposte in HTTPS e porte diverse da quelle utilizzate in passato.
  • Se la versione è successiva alla 7, ti basterà lanciare una semplice riconfigurazione.

Disinstallazione

Senza muoverti dalla cartella nella quale ti trovi via prompt dei comandi, esegui un amptools uninstall all-kuid per rimuovere l’agente e pulire ogni configurazione.

C:\Program Files (x86)\Dell\KACE>amptools uninstall all-kuid
[2017-11-06.12:49:31][AMPTools:StopIfNeeded ] [Stopping Service]
Stopping ..
AMPWatchDog stopped successfully
[2017-11-06.12:49:31][AMPTools:StopKonea ] [Stopping konea]
[2017-11-06.12:49:31][AMPTools:StopKonea ] Service "konea" stopped.
Stopping ..
AMPAgent stopped successfully
AMPWatchDog already stopped
DeleteSvcWithName:AMPWatchDog uninstalled.
[2017-11-06.12:49:45][AMPTools:RemoveKonea ] [Stopping konea]
[2017-11-06.12:49:45][AMPTools:RemoveKonea ] Service "konea" uninstalled.
Removing KUID from registryRemove the entire data directory

A questo punto, lancia l’installazione del pacchetto MSI in maniera silente. L’agente, non appena installato, contatterà nuovamente la console e invierà il suo inventario aggiornato. Nell’esempio di seguito, tengo conto di un’installazione dell’agente eseguita da una cartella locale del PC (l’agente è stato precedentemente copiato), nulla ti vieta di lanciarla da un’unità di rete che dovrà però essere accessibile anche da prompt dei comandi:

msiexec /i ampagent-7.2.10-x86_kace.contoso.com.msi /quiet /qn /norestart

Ti ricordo che, nel caso ti sfuggisse o non lo sapessi affatto, potrai specificare l’indirizzo del tuo Kace direttamente nel nome del pacchetto MSI, così da evitare che venga richiesto a video. Ti basterà posporre l’URL al nome pacchetto, subito prima dell’estensione. Guarda tu stesso l’esempio poco sopra: il pacchetto originale si chiamava ampagent-7.1.62-x86.msi, modificandolo in ampagent-7.1.62-x86_kace.contoso.com.msi, questo proverà immediatamente a contattare kace.contoso.com per inviare il suo inventario.

L’operazione dura un paio di minuti circa. A quel punto vedrai comparire il PC nella tua console Kace, e potrai cancellare il pacchetto MSI dalla cartella locale del PC (ammesso tu l’abbia copiato prima).

Riconfigurazione

La riconfigurazione è cosa assai più semplice, perché non richiede che un solo comando da eseguire via prompt, senza necessità di disinstallare alcunché. Anche se l’agente non è aggiornato rispetto a quello distribuito da console, ma è già compatibile con le nuove porte usate dal servizio Konea, penserà lui stesso ad aggiornarsi quando sarà nuovamente collegato al tuo Kace.

Sempre da prompt dei comandi (e dalla cartella di installazione dell’agente Kace) lancia un amptools resetconf host=kace.contoso.com (sostituendo ovviamente l’URL con quello della tua console), il servizio verrà così riavviato e partirà utilizzando il nuovo indirizzo fornito:

C:\Program Files (x86)\Dell\KACE>amptools resetconf host=kbox.contoso.com
[2017-11-08.09:23:16][AMPTools:AgentTools::PerformRese] Resetting config file C:\ProgramData\Dell\KACE\amp.conf
[2017-11-08.09:23:16][AMPTools:AgentTools::PerformRetr] Removing previous certificate (if it exists): C
[2017-11-08.09:23:16][AMPTools:AgentTools::StopIfNeede] [Stopping Service]
Stopping ..
AMPWatchDog stopped successfully
Stopping ...
OfflineScheduler stopped successfully
[2017-11-08.09:23:36][AMPTools:KoneaTools::StopKonea ] [Stopping konea]
[2017-11-08.09:23:36][AMPTools:KLaunchClient::Launch ] KLaunchClient::Launch()
[2017-11-08.09:23:36][AMPTools:KLaunchClient::DumpStat] Launch State: Command="C:\Program Files (x86)\Dell\KACE\konea.exe" Arguments="-stop" WorkingDir="" Timeout=14400000 ShouldDetach=0 ShowWindow=0 Redirect_stdout=1 Redirect_stderr=0 Desktop=winsta0\default Using Wait_Override=0
[2017-11-08.09:23:36][AMPTools:KLaunchUtils::DoesToken] DoesTokenHaveLSAPriviledges returning: 0
[2017-11-08.09:23:36][AMPTools:KLaunchClientImpl::Laun] KLaunchClientImpl::Launch() DoesCurrentProcessHaveLSAPriviledges = false
[2017-11-08.09:23:36][AMPTools:KLaunchClientImpl::Laun] KLaunchClientImpl::LaunchNoSession()
[2017-11-08.09:23:36][AMPTools:KLaunchClientImpl::Setu] KLaunchClientImpl::SetupStartInfo *Don't CreateStdPipe StdIn Pipe (disabled)
[2017-11-08.09:23:36][AMPTools:KLaunchClientImpl::Laun] KLaunchClientImpl::LaunchNoSession - CreateProcess returned successfully
[2017-11-08.09:23:36][AMPTools:KLaunchClientImpl::Read] KLaunchClientImpl::ReadStdPipe End of ReadStdPipe method
[2017-11-08.09:23:36][AMPTools:KLaunchClientImpl::Proc] KLaunchClientImpl::ProcessResults - End
[2017-11-08.09:23:36][AMPTools:KoneaTools::StopKonea ] Service "konea" stopped.
[2017-11-08.09:23:36][AMPTools:AgentTools::StartIfNeed] [Starting Service]
OfflineScheduler started successfully
AMPWatchDog started successfully
[2017-11-08.09:23:36][AMPTools:KoneaTools::StartKonea ] [Starting konea]
[2017-11-08.09:23:36][AMPTools:KLaunchClient::Launch ] KLaunchClient::Launch()
[2017-11-08.09:23:36][AMPTools:KLaunchClient::DumpStat] Launch State: Command="C:\Program Files (x86)\Dell\KACE\konea.exe" Arguments="-start" WorkingDir="" Timeout=14400000 ShouldDetach=0 ShowWindow=0 Redirect_stdout=1 Redirect_stderr=0 Desktop=winsta0\default Using Wait_Override=0
[2017-11-08.09:23:36][AMPTools:KLaunchUtils::DoesToken] DoesTokenHaveLSAPriviledges returning: 0
[2017-11-08.09:23:36][AMPTools:KLaunchClientImpl::Laun] KLaunchClientImpl::Launch() DoesCurrentProcessHaveLSAPriviledges = false
[2017-11-08.09:23:36][AMPTools:KLaunchClientImpl::Laun] KLaunchClientImpl::LaunchNoSession()
[2017-11-08.09:23:36][AMPTools:KLaunchClientImpl::Setu] KLaunchClientImpl::SetupStartInfo *Don't CreateStdPipe StdIn Pipe (disabled)
[2017-11-08.09:23:36][AMPTools:KLaunchClientImpl::Laun] KLaunchClientImpl::LaunchNoSession - CreateProcess returned successfully
[2017-11-08.09:23:36][AMPTools:KLaunchClientImpl::Read] KLaunchClientImpl::ReadStdPipe End of ReadStdPipe method
[2017-11-08.09:23:36][AMPTools:KLaunchClientImpl::Proc] KLaunchClientImpl::ProcessResults - End
[2017-11-08.09:23:36][AMPTools:KoneaTools::StartKonea ] Service "konea" started.

In conclusione

Se dovessero servirti (o sei semplicemente curioso) trovi le opzioni da riga di comando degli “amptools” nella documentazione prodotto, ma dovrai avere un’utenza registrata per poterla consultare: support.quest.com/kace-systems-management-appliance/kb/146458.

Salvo errori o modifiche non eseguite, tutto dovrebbe funzionare a pieno regime, consentendoti di riportare sulla terra ferma le macchine che navigano altrove perché senza una rotta certa. Certo inizialmente potresti avere molte segnalazioni e ti sembrerà di aver fatto una stupidata e che forse “occhio non vede, cuore non duole” è un gran proverbio, ma pensandoci a mente fredda forse mi ringrazierai :-)

In caso di dubbi, o se volessi dare ulteriori suggerimenti in merito, l’area commenti è sempre a tua totale disposizione.

Buon lavoro!

Correzioni, suggerimenti? Lascia un commento nell'apposita area qui di seguito o contattami privatamente.
Ti è piaciuto l'articolo? Offrimi un caffè! ☕ :-)

L'articolo potrebbe non essere aggiornato

Questo post è stato scritto più di 5 mesi fa, potrebbe non essere aggiornato. Per qualsiasi dubbio ti invito a lasciare un commento per chiedere ulteriori informazioni! :-)

Condividi l'articolo con i tuoi contatti:
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Commento
Oldest
Newest Most Voted
Inline Feedbacks
View all comments