I have written a Python script to get some Citrix performance data and use it to create graph with Grafana. The result looks like this :
The Python script uses the linux wmic client you can find here
The software requirements are the following :
- a debian / ubuntu system (you can choose another os but prefer those ones if you need help :))
- influxdb database
- grafana
- python (for me I have the version 2.7.3)
- InfluxDBClient for python
- wmic tool
For the moment, there are two Python script : one to get Citrix site info like number of Active/Disconnected sessions and one for performance info (user latency, session bandwidth,…)
In this example, you will have to create two influxdb databases called ctxmon and ctxusr
Here we go…
[config] FORESTNAME=domain.local WMIC=/usr/bin/wmic WMIUSR=wmiusername WMIPWD=wmipassword CDCSRV=10.11.0.1 DBSRVIP=10.11.0.2 DBSRVUSR=dbusr DBSRVPWD=dbpwd DB_USR_NAME=ctxusr WMICLASS=Win32_PerfFormattedData_CitrixICA_ICASession WMIOBJECT=InputClipboardBandwidt,InputDriveBandwidth,InputPrinterBandwidth,InputSessionBandwidth,InputSessionCompression,InputSessionLineSpeed,LatencyLastRecorded,LatencySessionAverage,LatencySessionDeviation,OutputPrinterBandwidth,OutputSeamlessBandwidth,OutputSessionBandwidth,OutputSessionCompression,OutputSessionLineSpeed,OutputThinWireBandwidth WMINAMESPACE=Root/CIMV2
#!/usr/bin/python from __future__ import division import socket import ConfigParser import os import subprocess import threading import re import time from influxdb import InfluxDBClient config = ConfigParser.RawConfigParser() config.read(os.path.dirname(os.path.realpath(__file__))+'/configusr.ini') forestname = config.get('config', 'FORESTNAME') wmic = config.get('config', 'WMIC') wmiusr = config.get('config', 'WMIUSR') wmipwd = config.get('config', 'WMIPWD') cdcsrv = config.get('config', 'CDCSRV') dbsrvip = config.get('config', 'DBSRVIP') dbsrvusr = config.get('config', 'DBSRVUSR') dbsrvpwd = config.get('config', 'DBSRVPWD') db_usr_name = config.get('config', 'DB_USR_NAME') wmiclass = config.get('config', 'WMICLASS') wmiobject = config.get('config', 'WMIOBJECT') wminamespace_ini = config.get('config', 'WMINAMESPACE') sct = re.compile("^mon---", re.IGNORECASE) client = InfluxDBClient(dbsrvip, 8096, dbsrvusr, dbsrvpwd, db_usr_name) monsct = filter(sct.search, config.sections()) threadLimiter = threading.BoundedSemaphore(value=200) class myThread (threading.Thread): def __init__(self, threadID, name, ctxuser, wmicmd, wmimoninfo): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.ctxuser = ctxuser self.wmicmd = wmicmd self.wmimoninfo = wmimoninfo def run(self): threadLimiter.acquire() try: print_adrepl(self.name, self.ctxuser, self.wmicmd, self.wmimoninfo ) finally: threadLimiter.release() def print_adrepl(threadName, ctxuser, wmicmd, wmimoninfo): json_body = [{ "points": [ [int(time.time()), ctxuser, int(wmicmd)] ], "name": wmimoninfo, "columns": ["time", "user", "value"] }] client.write_points(json_body, time_precision='s') threads = [] cred = '-U'+forestname+'/'+wmiusr+'%'+wmipwd wmidstip = "//"+cdcsrv wmiselect = "select * from Citrix_Server" wminamespace = "--namespace=Root/Citrix" wmidelimiter = "--delimiter=;" wmicmd = [wmic , cred , wmidstip , wmiselect , wminamespace , wmidelimiter ] proc = subprocess.Popen(wmicmd,stdout=subprocess.PIPE, stderr=subprocess.PIPE) outputlines = filter(lambda x:len(x)>0,(line.strip() for line in proc.stdout)) ctxlist = [] for ctxinfo in outputlines[5:]: if ctxinfo.split(';')[6] == zoneranking: ctxlist.append([ctxinfo.split(';')[2], ctxinfo.split(';')[4]]) while 1: i = 1 for ctxserver in ctxlist: wmimonvalue = 0 ctxname = ctxserver[1] ctxip = ctxserver[0] response = os.system("ping -c 1 -t 2 " + ctxip) if response == 0: wmidstip = "//" + ctxip wmiselect = "select "+wmiobject+" from "+wmiclass wminamespace = '--namespace='+wminamespace_ini wmidelimiter = "--delimiter=;" wmicmd = [wmic , cred , wmidstip , wmiselect , wminamespace , wmidelimiter ] print wmicmd proc = subprocess.Popen(wmicmd,stdout=subprocess.PIPE, stderr=subprocess.PIPE) outputlines = filter(lambda x:len(x)>0,(line.strip() for line in proc.stdout)) if ((not any("ERROR" in s for s in outputlines)) and outputlines): property_array = outputlines[1].split(";") for ctxuserinfo in outputlines[2:]: if (re.search(r"\(([A-Za-z0-9_]+)\)", ctxuserinfo.split(';')[9])): m = re.search(r"\(([A-Za-z0-9_]+)\)", ctxuserinfo.split(';')[9]) ctx_username = m.group(1) else: ctx_username = ctxuserinfo.split(';')[9].replace(" ", "") idx=2 for property_value in property_array[2:]: wmimonvalue = ctxuserinfo.split(';')[idx] if (idx != 9): #wmimoninfo = ctx_username + "." + ctxname + "." + property_value wmimoninfo = ctxname + "." + property_value thread = myThread(i, str(ctxname), ctx_username, wmimonvalue, wmimoninfo ) thread.start() threads.append(thread) idx += 1 i += 1 for t in threads: t.join() print "Exiting Main Thread"
[config] FORESTNAME=domain.local WMIC=/usr/bin/wmic WMIUSR=wmiusername WMIPWD=wmipassword CDCSRV=10.11.0.1 DBSRVIP=10.11.0.2 DBSRVUSR=dbusr DBSRVPWD=dbpwd DB_SESSION_NAME=ctxmon [mon---1] WMICLASS=MetaFrame_Server WMIINSTANCE= WMIOBJECT=NumberOfActiveSessions WMINAMESPACE=Root/Citrix [mon---2] WMICLASS=MetaFrame_Server WMIINSTANCE= WMIOBJECT=NumberOfDisconnectedSessions WMINAMESPACE=Root/Citrix [mon---3] WMICLASS=MetaFrame_Server WMIINSTANCE= WMIOBJECT=NumberOfSessions WMINAMESPACE=Root/Citrix
#!/usr/bin/python from __future__ import division import socket import ConfigParser import os import subprocess import threading import re import time from influxdb import InfluxDBClient config = ConfigParser.RawConfigParser() config.read(os.path.dirname(os.path.realpath(__file__))+'/config.ini') forestname = config.get('config', 'FORESTNAME') wmic = config.get('config', 'WMIC') wmiusr = config.get('config', 'WMIUSR') wmipwd = config.get('config', 'WMIPWD') cdcsrv = config.get('config', 'CDCSRV') dbsrvip = config.get('config', 'DBSRVIP') dbsrvusr = config.get('config', 'DBSRVUSR') dbsrvpwd = config.get('config', 'DBSRVPWD') db_session_name = config.get('config', 'DB_SESSION_NAME') sct = re.compile("^mon---", re.IGNORECASE) client = InfluxDBClient(dbsrvip, 8096, dbsrvusr, dbsrvpwd, db_session_name) monsct = filter(sct.search, config.sections()) threadLimiter = threading.BoundedSemaphore(value=200) class myThread (threading.Thread): def __init__(self, threadID, name, wmicmd, wmimoninfo): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.wmicmd = wmicmd self.wmimoninfo = wmimoninfo def run(self): threadLimiter.acquire() try: print_adrepl(self.name, self.wmicmd, self.wmimoninfo ) finally: threadLimiter.release() def print_adrepl(threadName, wmicmd, wmimoninfo): timeserie = threadName + '.' + wmimoninfo proc = subprocess.Popen(wmicmd,stdout=subprocess.PIPE, stderr=subprocess.PIPE) errorlines = filter(lambda x:len(x)>0,(line.strip() for line in proc.stderr)) if errorlines : t = 1 outputlines = filter(lambda x:len(x)>0,(line.strip() for line in proc.stdout)) if ((len(outputlines)>2) and (int(re.findall(r'\b\d+\b', outputlines[2:][0])[0])!=196)): json_body = [{ "points": [ [int(time.time()), int(re.findall(r'\b\d+\b', outputlines[2:][0])[0])] ], "name": timeserie, "columns": ["time", "value"] }] client.write_points(json_body, time_precision='s') threads = [] cred = '-U'+forestname+'/'+wmiusr+'%'+wmipwd wmidstip = "//"+cdcsrv wmiselect = "select * from Citrix_Server" wminamespace = "--namespace=Root/Citrix" wmidelimiter = "--delimiter=;" wmicmd = [wmic , cred , wmidstip , wmiselect , wminamespace , wmidelimiter ] proc = subprocess.Popen(wmicmd,stdout=subprocess.PIPE, stderr=subprocess.PIPE) outputlines = filter(lambda x:len(x)>0,(line.strip() for line in proc.stdout)) ctxlist = [] for ctxinfo in outputlines[5:]: ctxlist.append([ctxinfo.split(';')[2], ctxinfo.split(';')[4]]) while 1: i = 1 for ctxserver in ctxlist: ctxname = ctxserver[1] ctxip = ctxserver[0] response = os.system("ping -c 1 -t 2 " + ctxip) if response == 0: wmidstip = "//" + ctxip for monitor in monsct : if config.get(monitor, 'WMIINSTANCE') != "" : wmiinstance = " where name='"+config.get(monitor, 'WMIINSTANCE')+"'" else : wmiinstance = " " wmiselect = "select " + config.get(monitor, 'WMIOBJECT') + " from " + config.get(monitor, 'WMICLASS') + wmiinstance wminamespace = "--namespace=" + config.get(monitor, 'WMINAMESPACE') wmidelimiter = "--delimiter=;" wmicmd = [wmic , cred , wmidstip , wmiselect , wminamespace , wmidelimiter ] wmimoninfo = config.get(monitor, 'WMICLASS')+"_"+config.get(monitor, 'WMIOBJECT') thread = myThread(i, str(ctxname), wmicmd, wmimoninfo ) thread.start() threads.append(thread) i += 1 time.sleep(30) for t in threads: t.join() print "Exiting Main Thread"
Last step, build the graphs
I have used the templating feature in Grafana to filter data using variables : one variable for the Citrix server name and the other for the username
You can download the following file and import it as a new dashboard :
Citrix-Session-Monitoring
A new feature has been added : annotation
Here an annotation is added when the Latency average is greater than 10s (=10000). You can adapt this value with your environment.
Do not hesitate to leave a comment if you need help…
My Powershell script categories
- Active Directory
- Cluster
- Database
- Exchange
- Files and folders
- Hardware
- Network
- Operating System
- PKI
- SCCM
- Service and process
- Tips
- VMWare
Hi Nicolas,
I’m always getting exiting main thread when running one of the python scripts, but no data is sent to influxdb.
Is there a way to debug the scripts? Already tried but am getting lost, since python is not a language I know very well.
Thanks in advance Johannes
Hi Nicolas,
I’m always getting exiting main thread when running one of the python scripts, but no data is sent to influxdb.
Is there a way to debug the scripts? Already tried but am getting lost, since python is not a language I know very well.
Thanks in advance Johannes
Hi. I realize this post is quite old, but.. I am trying to run the first python script and receiving the following error message. Very new to python so not sure what I am missing. Would greatly appreciate any help.
Traceback (most recent call last):
File “./ctxsession.py”, line 68, in
proc = subprocess.Popen(wmicmd,stdout=subprocess.PIPE, stderr=subprocess.PIPE)
File “/usr/lib/python2.7/subprocess.py”, line 394, in __init__
errread, errwrite)
File “/usr/lib/python2.7/subprocess.py”, line 1047, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory
Hello Brandi,
The error is certainly caused by the missing of wmic tool on your box. Did you try to install it using one of this link :
– https://medium.com/@alberto.schiabel/how-to-compile-wmic-on-a-modern-gnu-linux-system-d4b0783bbf64
– https://www.aldeid.com/wiki/Wmic-linux
Keep me informed.
Sorry for the late feedback
Hi. I realize this post is quite old, but.. I am trying to run the first python script and receiving the following error message. Very new to python so not sure what I am missing. Would greatly appreciate any help.
Traceback (most recent call last):
File “./ctxsession.py”, line 68, in
proc = subprocess.Popen(wmicmd,stdout=subprocess.PIPE, stderr=subprocess.PIPE)
File “/usr/lib/python2.7/subprocess.py”, line 394, in __init__
errread, errwrite)
File “/usr/lib/python2.7/subprocess.py”, line 1047, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory
Hello Brandi,
The error is certainly caused by the missing of wmic tool on your box. Did you try to install it using one of this link :
– https://medium.com/@alberto.schiabel/how-to-compile-wmic-on-a-modern-gnu-linux-system-d4b0783bbf64
– https://www.aldeid.com/wiki/Wmic-linux
Keep me informed.
Sorry for the late feedback