-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrunRemoteLoadtest.py
More file actions
executable file
·219 lines (197 loc) · 8.14 KB
/
runRemoteLoadtest.py
File metadata and controls
executable file
·219 lines (197 loc) · 8.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#!/usr/bin/env python3
"""
runs a loadtest using Neocortix loadtest service
"""
# standard library modules
import argparse
#import concurrent.futures
import csv
import datetime
import json
import logging
import os
import random
import re
import requests
import shutil
import subprocess
import sys
import time
# third-party modules
#import dateutil
#import dateutil.parser
#import pandas as pd
# neocortix modules
logger = logging.getLogger(__name__)
def startTest( testsUrl, reqParams ):
reqDataStr = json.dumps( reqParams )
#logger.debug( 'reqDataStr: %s', reqDataStr )
resp = requests.post( testsUrl, data=reqDataStr )
logger.info( 'POST status_code %d', resp.status_code )
logger.info( 'POST text %s', resp.text )
respJson = resp.json()
#logger.info( 'POST json %s', respJson )
testId = respJson['id']
return testId
def downloadDataFile(url, dataDirPath):
local_filename = dataDirPath + '/' + url.split('/')[-1]
# make request with stream=True
with requests.get(url, stream=True) as r:
r.raise_for_status()
with open(local_filename, 'wb') as f:
for chunk in r.iter_content(chunk_size=8192):
if chunk: # filter out keep-alive new chunks
f.write(chunk)
# f.flush()
return
def genXml():
'''preliminary version generates "fake" junit-style xml'''
template = '''<?xml version="1.0" ?>
<testsuites>
<testsuite tests="1" errors="0" failures="0" name="loadtests" >
<testcase classname="com.neocortix.loadtest" name="loadtest" time="1.0">
<system-out>
I am stdout!
</system-out>
<system-err>
I am stderr!
</system-err>
</testcase>
</testsuite>
</testsuites>
'''
return template
if __name__ == "__main__":
logging.basicConfig(format='%(asctime)s %(levelname)s %(module)s %(funcName)s %(message)s', datefmt='%Y/%m/%d %H:%M:%S')
logger.setLevel(logging.INFO)
logger.debug('the logger is configured')
ap = argparse.ArgumentParser( description=__doc__, fromfile_prefix_chars='@' )
ap.add_argument( 'victimHostUrl', help='url of the host to target as victim' )
ap.add_argument( '--authToken', required=True, help='the NCS authorization token to use' )
ap.add_argument( '--altTargetHostUrl', help='an alternative target host URL for comparison' )
ap.add_argument('--jsonOut', help='file path to write detailed info in json format')
ap.add_argument( '--masterUrl', default='http://localhost', help='url of the master' )
ap.add_argument( '--nWorkers', type=int, default=1, help='the # of worker instances to launch (or zero for all available)' )
ap.add_argument( '--rampUpRate', type=float, default=0, help='# of simulated users to start per second (overall)' )
ap.add_argument( '--regions', nargs='*', help='list of geographic regions (or none for all regions)' )
ap.add_argument( '--susTime', type=int, default=10, help='time to sustain the test after startup (in seconds)' )
ap.add_argument( '--targetUris', nargs='*', help='list of URIs to target' )
ap.add_argument( '--usersPerWorker', type=int, default=6, help='# of simulated users per worker' )
args = ap.parse_args()
dataDirPath = 'data'
os.makedirs( dataDirPath, exist_ok=True )
logger.info( 'args.altTargetHostUrl: %s', args.altTargetHostUrl )
masterUrl = args.masterUrl
logger.info( 'testing connectivity to masterUrl: %s', masterUrl )
# get '/' to test connectivity
resp = requests.get( masterUrl+'/' )
logger.info( 'resp.status_code %d', resp.status_code )
# dont' print the text; that would be the web page source
#logger.info( 'resp.text %s', resp.text )
testsUrl = masterUrl+'/api/tests/'
# get /tests/
resp = requests.get( testsUrl )
logger.info( '/api/tests/ status_code %d', resp.status_code )
#logger.info( '/api/tests/ json %s', resp.json() )
# set params for tests
nWorkers = args.nWorkers
startTimeLimit = 30
susTime = args.susTime
usersPerWorker = args.usersPerWorker
rampUpRate = args.rampUpRate
reqParams = [args.victimHostUrl, "<MasterHostUnspecified>",
"--authToken", args.authToken, "--nWorkers", str(nWorkers),
"--susTime", str(susTime), "--usersPerWorker", str(usersPerWorker),
"--rampUpRate", str(rampUpRate), "--startTimeLimit", str(startTimeLimit)
]
if args.altTargetHostUrl:
reqParams.append( '--altTargetHostUrl' )
reqParams.append( args.altTargetHostUrl )
if args.targetUris:
reqParams.append( '--targetUris' )
for uri in args.targetUris:
reqParams.append( uri )
if args.regions:
regionsDict = {'regions': args.regions}
filterArg = json.dumps( regionsDict )
# this code could become trickier if other filter args are supported
reqParams.append( '--filter' )
reqParams.append( filterArg )
# start test
testId = startTest( testsUrl, reqParams )
logger.info( 'testId: %s', testId )
# result dict from the service
result = {}
# poll the started tests
while True:
anyRunning = False
statusUrl = testsUrl + testId
logger.info( 'polling: %s', statusUrl )
resp = requests.get( statusUrl )
if resp.status_code != 200:
logger.warning( 'poll status_code %d', resp.status_code )
else:
respJson = resp.json()
result = respJson
logger.info( 'poll json state: %s', respJson['state'] )
logger.info( 'poll json stderr: %s',
'\n'.join( respJson['stderr'].splitlines()[-5:] ) )
anyRunning = anyRunning or respJson['state'] == 'running'
#if respJson['state'] == 'stopped':
# break
if not anyRunning:
break
time.sleep( 5 )
# check result
success = False
if not result:
print( '>>NO result for', testId )
else:
if result.get('stdout'):
success = True
print('>>stdout from', testId)
print( result['stdout'] )
else:
print('>>NO stdout from', testId)
if success:
dataUrlPrefix = testsUrl + testId
if args.altTargetHostUrl:
try:
downloadDataFile( dataUrlPrefix + '/ltStats_a.html', dataDirPath )
except Exception as exc:
logger.warning( 'exception (%s) downloading; %s', type(exc), exc )
try:
downloadDataFile( dataUrlPrefix + '/ltStats_b.html', dataDirPath )
except Exception as exc:
logger.warning( 'exception (%s) downloading; %s', type(exc), exc )
else:
try:
downloadDataFile( dataUrlPrefix + '/ltStats.html', dataDirPath )
except Exception as exc:
logger.warning( 'exception (%s) downloading; %s', type(exc), exc )
try:
downloadDataFile( dataUrlPrefix + '/locustStats.jlog', dataDirPath )
except Exception as exc:
logger.warning( 'exception (%s) downloading; %s', type(exc), exc )
try:
downloadDataFile( dataUrlPrefix + '/integratedPerf.png', dataDirPath )
except Exception as exc:
logger.warning( 'exception (%s) downloading; %s', type(exc), exc )
if False:
# generate fake junit-style xml test result file
xmlOut = genXml()
junitResultDirPath = dataDirPath + '/test-results/loadtest'
junitResultFilePath = junitResultDirPath + '/results.xml'
os.makedirs( junitResultDirPath, exist_ok=True )
with open( junitResultFilePath, 'w', encoding='utf8') as outFile:
outFile.write( xmlOut )
# save detailed outputs, if requested
if args.jsonOut:
argsToSave = vars(args).copy()
del argsToSave['authToken']
toSave = { 'args': argsToSave, 'result': result }
jsonOutFilePath = os.path.expanduser( os.path.expandvars( args.jsonOut ) )
with open( jsonOutFilePath, 'w') as outFile:
json.dump( toSave, outFile, indent=2 )
#json.dump( toSave, outFile, default=str, indent=2, skipkeys=True )
sys.exit( not success )