Knowledge Base > Article [0024]

Stress Test

You can use this python utility to test the DUAP platform performance - authentication capacity.

Make sure the following things ready.

  • DUAP server installed on Windows 2003 server,
  • Modify TypeSense similarity in Home - System Settings - Authenticator - Typesense - Info,
  • Install Python 2.5 (or later) on the machine (XP SP3) where you are going to run the utility,
  • Regsiter the com dll das.dll on the same machine.

Then run the following command in Windows Prompt.

stresstest.py -s 192.168.222.11 -p 8080 -d -t -c 5 -r 2

You can adjust the parameters, please run "stresstest.py -h" to see what they mean.

The result sample

Server URL: http://192.168.222.11:8080/das/xmlrpc

Initializing Tests

        Creating application... OK
        Creating account... OK
        Creating TypeSense token... OK
        Creating DevicePass token... OK

Training TypeSense token (77004276)... . . . . . . . . .
Status: ACTIVE, Training: 9, Progress: 9

Start 2 tests with 5 threads per test
Number of requests per thread: 2

    Thread     Result     Serial       Type
---------------------------------------------------
       228 E-CREDENTIAL-INCORRECT   77004277      DevicePass
      1968       TRUE   77004276      TypeSense
      5156 E-CREDENTIAL-INCORRECT   77004277      DevicePass
      5288       TRUE   77004276      TypeSense
      5996 E-CREDENTIAL-INCORRECT   77004277      DevicePass
      4416       TRUE   77004276      TypeSense
      4828       TRUE   77004276      TypeSense
      4732 E-CREDENTIAL-INCORRECT   77004277      DevicePass
       228       TRUE   77004277      DevicePass
      1968       TRUE   77004276      TypeSense
      5288       TRUE   77004276      TypeSense
      5156 E-CREDENTIAL-INCORRECT   77004277      DevicePass
      4504       TRUE   77004276      TypeSense
      5996 E-CREDENTIAL-INCORRECT   77004277      DevicePass
      4828       TRUE   77004276      TypeSense
      5096 E-CREDENTIAL-INCORRECT   77004277      DevicePass
      4416       TRUE   77004276      TypeSense
      4732 E-CREDENTIAL-INCORRECT   77004277      DevicePass
      4504       TRUE   77004276      TypeSense
      5096 E-CREDENTIAL-INCORRECT   77004277      DevicePass
---------------------------------------------
Time: 0.636563267456 seconds    Requests: 20    Requests/Time: 31.418715189

The Source Code

import sys
import win32com.client
import datetime
import random
import time
import threading
import thread
from optparse import OptionParser

creds = [
	'90-1-0#90-2-121#69-1-228#69-2-322#78-1-343#78-2-421#71-1-445#71-2-525#89-1-580#89-2-671#73-1-769#73-2-858#9-1-959#9-2-858#80-1-1068#80-2-1151#65-1-1282#65-2-1368#83-1-1466#83-2-1541#83-1-1633#83-2-1711#80-1-1801#79-1-1944#80-2-1959#82-1-2078#79-2-2086#82-2-2163#84-1-2248#84-2-2350',
	'90-1-0#90-2-110#69-1-217#69-2-322#78-1-338#78-2-427#71-1-459#71-2-564#89-1-586#89-2-672#73-1-767#73-2-861#9-1-932#9-2-861#80-1-1025#80-2-1116#65-1-1198#65-2-1284#83-1-1396#83-2-1476#83-1-1558#83-2-1663#80-1-1728#80-2-1822#79-1-1882#79-2-1988#82-1-2042#82-2-2119#84-1-2207#84-2-2304',
	'90-1-0#90-2-111#69-1-228#69-2-339#78-1-361#78-2-476#71-1-512#71-2-606#89-1-647#89-2-733#73-1-839#73-2-928#9-1-1012#9-2-928#80-1-1163#80-2-1248#65-1-1325#65-2-1396#83-1-1508#83-2-1586#83-1-1687#83-2-1770#80-1-1851#80-2-1948#79-1-2008#79-2-2113#82-1-2141#82-2-2226#84-1-2311#84-2-2396',
	'90-1-0#90-2-105#69-1-225#69-2-316#78-1-343#78-2-448#71-1-525#89-1-627#71-2-649#89-2-707#73-1-806#73-2-897#9-1-973#9-2-897#80-1-1115#80-2-1184#65-1-1307#65-2-1356#83-1-1477#83-2-1543#83-1-1641#83-2-1721#80-1-1836#80-2-1905#79-1-1962#79-2-2081#82-1-2111#82-2-2191#84-1-2267#84-2-2370',
	'90-1-0#90-2-85#69-1-222#69-2-299#78-1-365#78-2-448#71-1-480#71-2-566#89-1-637#89-2-731#73-1-829#73-2-912#9-1-975#9-2-912#80-1-1119#80-2-1214#65-1-1295#65-2-1373#83-1-1474#83-2-1551#83-1-1644#83-2-1729#80-1-1798#80-2-1883#79-1-1943#79-2-2054#82-1-2098#82-2-2178#84-1-2284#84-2-2361',
	'90-1-0#90-2-119#69-1-234#69-2-314#78-1-330#78-2-413#71-1-441#71-2-537#89-1-586#89-2-669#73-1-770#73-2-848#9-1-902#9-2-848#80-1-1028#80-2-1108#65-1-1198#65-2-1281#83-1-1377#83-2-1446#83-1-1536#83-2-1616#80-1-1698#80-2-1781#79-1-1857#79-2-1970#82-1-2019#82-2-2102#84-1-2181#84-2-2298',
	'90-1-0#90-2-91#69-1-222#69-2-311#78-1-324#78-2-410#71-1-456#71-2-553#89-1-578#89-2-672#73-1-770#73-2-853#9-1-913#80-1-853#80-2-936#65-1-1007#65-2-1076#83-1-1180#83-2-1257#83-1-1358#83-2-1424#80-1-1484#80-2-1570#79-1-1619#79-2-1749#82-1-1776#82-2-1867#84-1-1946#84-2-2049',
	'90-1-0#90-2-75#69-1-217#78-1-316#69-2-330#78-2-416#71-1-438#71-2-554#89-1-576#89-2-653#73-1-749#73-2-840#9-1-911#9-2-840#80-1-1029#80-2-1114#65-1-1188#65-2-1271#83-1-1374#83-2-1441#83-1-1547#83-2-1602#80-1-1698#80-2-1795#79-1-1863#79-2-1965#82-1-2003#82-2-2094#84-1-2187#84-2-2278',
	'90-1-0#90-2-86#69-1-280#69-2-374#78-1-393#78-2-481#71-1-497#71-2-583#89-1-657#89-2-729#73-1-816#73-2-896#9-1-989#9-2-896#80-1-1125#80-2-1218#65-1-1273#65-2-1362#83-1-1457#83-2-1518#83-1-1614#83-2-1699#80-1-1748#80-2-1837#79-1-1894#79-2-2005#82-1-2029#82-2-2118#84-1-2199#84-2-2296',
	'90-1-0#90-2-107#69-1-186#69-2-272#78-1-291#78-2-376#71-1-406#71-2-503#89-1-525#89-2-604#73-1-692#73-2-783#9-1-876#9-2-783#80-1-991#80-2-1074#65-1-1139#65-2-1217#83-1-1320#83-2-1384#83-1-1471#83-2-1537#80-1-1663#80-2-1746#79-1-1789#79-2-1889#82-1-1976#82-2-2056#84-1-2201#84-2-2298'
]

oManager = win32com.client.Dispatch("DAS.Manager")
oAuth = win32com.client.Dispatch("DAS.Authenticator")
uid='testaccount-001'
loginName='testaccount'
appid='STEST'
tdid='8438C8D3-CF01-45c8-BAA8-1A9E7CE36344'
ip='127.0.0.1'
serial=''

formats = "%10d %10s %10s %15s"

deviceCredential='''<credential><method>DevicePass</method><credentialData><devicePrint>y02FyQz/HwxDC8R9mOXTAn52TlNECTyFue6JrHDAAZ8qtszQE6yw8XWNxI/d2l/qrMt9zrJ6
ngbYJfgMLevuhMI3TvWZHT8FydSEZHO5CcqDmSUEnNwNYAtFj4stTp6OEdrQ1Y5IXl0/Sp/0
Z+mY8pwY9l9HvFp2Elm6+P2UGGYJzVsS5LQ5eZ/f+oJiUggYfZjyU4BGD7AHmNHVI26RMkdd
d0dlj93ZnDMfQyVsuUvnnWCmQvuCfIG77mECxlXvt7ZfM4njvVebrnHm8gwCxx6bXR9uRGuJ
thU96sfLnC+qWyzeDTved4xLQU95pnoMbGjonPah4519CWqquXfsHslY6zGa7KU7AqLpqO/K
IrzgCvHGl/j0dv6Es1/q6Pj1VCrsd8QDXH8cLfRnru/2jdTOqGC38L7YsW5BnwEW52Hlyy9K
nZSP</devicePrint></credentialData></credential>'''

devicePrint = '''<credential><method>DevicePass</method><token><deviceID>%s</deviceID><class>%s</class></token><credentialData>
<devicePrint>y02FyQz/HwxDC8Rlz+rsK3xFTAJmHx6o2fi9l0zYD5oYk8KPKYKw8XWNxI/d2l/qrMt9zrJ6njOnA5ZNZo6gxsUkCYyeXlBe+K+AZ2ijNee3vyUEnNwNYD46qeVsBfvAU93DkvdPHTJke+TwZPKCzrEs0F9HvFp2EmzF3pPVUwNHj1wBo80+OvCEy/lmURMCQbXGdYBGD7AHmOSqBQDQeSITNUB2yKTe31xEcl5oulD9oU2SZPuCfIG77lR94Duu/NMRcY7w+i6c7R69w3cGxAWBYTJaYmuJthU96vK0ukHrEEmQTzzNMPVMAiAilwEIb3PyoNuVxZ19CWqquUKTOKcZoFTUrqIoRdvu64CRE8fkCercq9XAUP6Es1/q6M2KckStPKFNHngPao1g7YCtvK/Kq3utzJPsl25BnwEW52Hlyy9KnZSP</devicePrint></credentialData></credential>''' % (tdid, ip)

counter_lock = thread.allocate_lock()
g_reqs = 0

def increaseCounter():
	try: # Entering critical section.		
		counter_lock.acquire()		
		global g_reqs
		g_reqs += 1		
	finally:
		counter_lock.release()

def generateTypeprint(index):
	typeprint = creds[index]
	
	credential ='<?xml version="1.0"?>'
	credential += '<credential>'
	credential += '<method>Typesense</method>'
	credential += '<credentialData>'
	credential += '<typetext></typetext>'
	credential += '<typeprint>'+typeprint+'</typeprint>'
	credential += '</credentialData>'
	credential += '</credential>'
	return credential
	
def testTypeSense(reqs):	
	n = len(creds)
	
	for i in xrange(reqs):	
		cred = generateTypeprint(i%n)
		result = oAuth.verifyCredential(appid, uid, cred)		
		if options.verbose:
			print formats % (thread.get_ident(), oAuth.LastError, result.Serial, 'TypeSense ')#, result.Score
		increaseCounter()
		
		
def trainToken():
	n = len(creds)
	print 'Training TypeSense token (%s)...' % serial,
	for i in range(1,n):
		cred = generateTypeprint(i)
		result = oManager.trainToken(serial, cred)		
		print '.',
	print
		
	result = oManager.checkToken(serial)
	if oManager.LastError!='OK':
		print oManager.LastError
		return False
		
	print 'Status: %s, Training: %d, Progress: %d' % (result.Status, result.Training, result.Progress)
	return True
	
		
def testDevicePass(reqs):
	for i in xrange(reqs):
		result = oAuth.verifyCredential2('1234567890',appid,uid,devicePrint)		
		if options.verbose:
			print formats % (thread.get_ident(), oAuth.LastError, result.Serial, 'DevicePass')
		increaseCounter()		

		
class TypeSenseTestThread(threading.Thread):
	def __init__(self, r):
		threading.Thread.__init__(self)
		self.reqs = r
	def run(self):		
		testTypeSense(self.reqs)
		
class DevicePassTestThread(threading.Thread):
	def __init__(self, r):
		threading.Thread.__init__(self)
		self.reqs = r
	def run(self):		
		testDevicePass(self.reqs)
		
def createEnv():
	oApplication = win32com.client.Dispatch("DAS.Application")
	oApplication.Name = "Stress Test"
	oApplication.AppID = appid
	#oApplication.AppKey = "12345678"
	
	print
	print 'Initializing Tests'
	print 
	print '\tCreating application...',
	
	result = oManager.createApp(oApplication)
	print oManager.LastError
	if not result:		
		return False
		
	oAccount = win32com.client.Dispatch("DAS.Account")
	oAccount.AppID = appid
	oAccount.UserID =uid
	oAccount.UserName = "Test User"
	oAccount.LoginName = loginName
	
	print '\tCreating account...',
	result = oManager.createAccount(oAccount)
	print oManager.LastError
	if not result:		
		return False
		
	if options.typesense:
		print '\tCreating TypeSense token...',
		oToken = win32com.client.Dispatch("DAS.Token")
		oToken.AppID = appid
		oToken.UserID = uid
		oToken.Method = 'TypeSense'
		oToken.Name = 'TestTypeSenseToken'
		oToken.Description = 'Typesense Token created by stress test'
		
		result = oManager.createToken(oToken, True)
		print oManager.LastError
		if result is None:		
			return False
		global serial
		serial = result
	
	if options.devicepass:
		print '\tCreating DevicePass token...',
		oToken = win32com.client.Dispatch("DAS.Token")
		oToken.AppID = appid
		oToken.UserID = uid
		oToken.Method = 'DevicePass'
		oToken.Name = 'TestDevicePassToken'
		oToken.Description = 'DevicePass Token created by stress test'
		oToken.Credential = deviceCredential
		oToken.Class = ip
		oToken.DeviceID = tdid
		
		result = oManager.createToken(oToken, True)
		print oManager.LastError
		if result is None:		
			return False
		
	print
		
def clearupEnv():
	oManager.deleteAccount(appid,uid)
	oManager.deleteApp(appid)

def main():
	
	parser = OptionParser()
	parser.add_option("-s", "--server", dest="serverIP", default="127.0.0.1",
	                  help="IP address of Deepnet Authentication Server", metavar="IP")
	parser.add_option("-p", "--port", dest="serverPort",default=8080,type='int',
	                  help="Port number of Deepnet Authentication Server", metavar="PORT")
	parser.add_option("-t", "--typesense", dest="typesense",default=False, action='store_true',
	                  help="Do TypeSense tests")
	parser.add_option("-d", "--devicepass", dest="devicepass",default=False, action='store_true',
	                  help="Do DevicePass tests")
	parser.add_option("-c", dest="threads",default=10, type='int', metavar="THREADS",
	                  help="Number of concurrent threads for each tests")
	parser.add_option("-r", dest="requests",default=10, type='int', metavar="REQUESTS",
	                  help="Number of requests per thread")
	parser.add_option("-q", "--quiet",
	                  action="store_false", dest="verbose", default=True,
	                  help="don't print verify credential details to stdout")
	                  
	global options
	(options, args) = parser.parse_args()
	
	tests = 0
	if options.typesense:
		tests=tests+1
	if options.devicepass:
		tests=tests+1
		
	if tests==0:
		parser.print_help()
		sys.exit(2)
		
	global oManager, oAuth
	url = 'http://%s:%d/das/xmlrpc' % (options.serverIP, options.serverPort)
	
	print 'Server URL:', url
	oManager.ServerURI = url
	oAuth.ServerURI = url
	
	try:
		result = oManager.hello()
		if oManager.LastError !='OK':
			print 'Connect to server failed:',oManager.LastError
			sys.exit(1)
	except Exception, err:
		sys.stderr.write('ERROR: %s\n' % str(err))
		sys.exit(1)
			
	try:
		createEnv()
		
		if options.typesense:
			trainToken()
			
		time.clock()	
		thrdgrp=list()
		
		
		print 
		if tests>1:
			print 'Start %d tests with %d threads per test' % (tests,options.threads)
		else:
			print 'Start %d test with %d threads per test' % (tests,options.threads)
		print 'Number of requests per thread: %d' % options.requests
		print
		
		
		
		if options.verbose and tests!=0:
			print "%10s %10s %10s %10s" % ('Thread', 'Result', 'Serial', 'Type')
			print '---------------------------------------------------'
		
		if options.typesense:
			for i in range(options.threads):
				m=TypeSenseTestThread(options.requests)
				m.start()
				thrdgrp.append(m)
		
		if options.devicepass:
			for i in range(options.threads):
				m=DevicePassTestThread(options.requests)
				m.start()
				thrdgrp.append(m)	
		
		for t in thrdgrp:
			t.join()
			
		T = time.clock()
		if options.verbose and tests!=0:
			print '---------------------------------------------'
		print "Time:", T, "seconds\tRequests:", g_reqs, "\tRequests/Time:", g_reqs/T
	finally:
		clearupEnv()
	
main()