How can we terminate the locust process based on num of requests? - performance-testing

I have to do load test using locust.io for multiple APIs, so I want to execute 10 requests for each and every API (suppose I have 10 APIs , so each and every API should get 10 requests) and should terminate the process automatically if all requests were done.
Below is the code snippet i have tried using for loop but not yet worked.
class UserBehavior(TaskSet):
def __init__(self, parent):
super().__init__(parent)
self.camp_code = random.choice(t.camp_code)
self.pgm_id = random.choice(t.pgm_id)
#task
def alloction_details(self):
pgm_id = self.pgm_id
camp_code = self.camp_code
print("L21",camp_code)
myheaders = {'Content-Type': 'application/json', 'Accept': 'application/json'}
load = {
"Code": str(camp_code),
"Id": str(pgm_id)}
# print("L43", users)
for i in range(10):
if i==9:
self.interrupt()
else:
with self.client.post('/ContentDetails', data= json.dumps(load), headers=myheaders,catch_response=True) as response:
print(response.status_code)
if response.status_code==200:
response.success()
# res = response.json()
print("Response of allocation details ", response.text, '\n\n')
else:
response.failure('API failure')
#task
def get_content_details(self):
myheaders = {'Content-Type': 'application/json', 'Accept': 'application/json'}
pgm_id = self.pgm_id
camp_code = self.camp_code
for i in range(10):
if i == 9:
# sleep(1000)
self.interrupt()
else:
with self.client.post('/ScheduleDetails?Code='+str(camp_code), catch_response=True) as response:
print(response.status_code)
if response.status_code==200:
response.success()
# res = response.json()
print("Response : ", response.text, '\n\n')
else:
response.failure('API failure')
class WebsiteUser(HttpUser):
tasks = [UserBehavior]
host = 'localhost'
wait_time = between(1, 3)

Related

bithumb Api error -{'status': '5100', 'message': 'Bad Request.(Auth Data)'}

I wanted to make a program where I can check the order details of my order at Bithumb Exchange.
So I looked at the docs(https://api.bithumb.com/info/orders) and made it, but the same error code kept coming out, so I don't know what to do.
import time
import math
import base64
import hmac, hashlib
import urllib.parse
import requests
class XCoinAPI:
api_url = "https://api.bithumb.com";
api_key = "";
api_secret = "";
def __init__(self, api_key, api_secret):
self.api_key = api_key;
self.api_secret = api_secret;
def body_callback(self, buf):
self.contents = buf;
def microtime(self, get_as_float = False):
if get_as_float:
return time.time()
else:
return '%f %d' % math.modf(time.time())
def usecTime(self) :
mt = self.microtime(False)
mt_array = mt.split(" ")[:2];
return mt_array[1] + mt_array[0][2:5];
def xcoinApiCall(self, endpoint, rgParams):
# 1. Api-Sign and Api-Nonce information generation.
# 2. Request related information from the Bithumb API server.
#
# - nonce: it is an arbitrary number that may only be used once.
# - api_sign: API signature information created in various combinations values.
endpoint_item_array = {
"endpoint" : endpoint
}
uri_array = dict(endpoint_item_array, **rgParams) # Concatenate the two arrays.
str_data = urllib.parse.urlencode(uri_array)
nonce = self.usecTime()
data = endpoint + chr(1) + str_data + chr(1) + nonce
utf8_data = data.encode('utf-8')
key = self.api_secret
utf8_key = key.encode('utf-8')
h = hmac.new(bytes(utf8_key), utf8_data, hashlib.sha512)
hex_output = h.hexdigest()
utf8_hex_output = hex_output.encode('utf-8')
api_sign = base64.b64encode(utf8_hex_output)
utf8_api_sign = api_sign.decode('utf-8')
headers = {
"Accept": "application/json",
"Content-Type": "application/x-www-form-urlencoded",
"Api-Key": self.api_key,
"Api-Nonce": nonce,
"Api-Sign": utf8_api_sign
}
url = self.api_url + endpoint
r = requests.post(url, headers=headers, data=uri_array)
return r.json()
a = XCoinAPI(api_key="MYKEY1c", api_secret="MYKEY2")
aa= a.xcoinApiCall("/info/orders",{"order_currency":"LN","payment_currency":"BTC"})
print(aa)
{'status': '5100', 'message': 'Bad Request.(Auth Data)'}
Process finished with exit code 0
The error code 5100, bad request keeps coming up(https://apidocs.bithumb.com/docs/api-%EC%A3%BC%EC%9A%94-%EC%97%90%EB%9F%AC-%EC%BD%94%EB%93%9C)
I really don't know which code to modify.
I think it's a parameter problem with XcoinApiCall, but I don't know.

count successful and unsuccessful post requests for asynchronous post call/request

I need help in implementing the logic to count number of successful post calls which are asynchronous in nature (status_code=200) as well as failed_calls (status_code != 200)
I am new to coroutines. Would appreciate if someone can suggest a better way of making a post asynchronous call which can be retried, polled for status, and that can emit metrics for successful post requests as well.
Following is my code:
asyncio.get_event_loop().run_in_executor(
None,
self.publish_actual,
event_name,
custom_payload,
event_message_params,
)
which calls publish_actual:
def publish_actual(
self,
event_name: str,
custom_payload={},
event_message_params=[],
):
"""Submits a post request using the request library
:param event_name: name of the event
:type event_name: str
:param key: key for a particular application
:param custom_payload: custom_payload, defaults to {}
:type custom_payload: dict, optional
:param event_message_params: event_message_params, defaults to []
:type event_message_params: list, optional
"""
json_data = {}
path = f"/some/path"
self.request(path, "POST", json=json_data)
which calls following request function
def request(self, api_path, method="GET", **kwargs):
try:
self._validate_configuration()
headers = {}
api_endpoint = self.service_uri.to_url(api_path)
logger.debug(api_endpoint)
if "headers" in kwargs and kwargs["headers"]:
headers.update(kwargs["headers"])
headers = {"Content-Type": "application/json"}
begin = datetime.now()
def build_success_metrics(response, *args, **kwargs):
tags = {
"name": "success_metrics",
"domain": api_endpoint,
"status_code": 200,
}
build_metrics(tags)
def check_for_errors(response, *args, **kwargs):
response.raise_for_status()
response = self.session.request(
method=method,
url=api_endpoint,
headers=headers,
timeout=self.timeout,
hooks={"response": [build_success_metrics, check_for_errors]},
**kwargs,
)
end = datetime.now()
logger.debug(
f"'{method}' request against endpoint '{api_endpoint}' took {round((end - begin).total_seconds() * 1000, 3)} ms"
)
logger.debug(f"response: {response}")
except RequestException as e:
tags = {
"name": "error_metrics",
"domain": api_endpoint,
"exception_class": e.__class__.__name__,
}
build_metrics(tags)
return f"Exception occured: {e}"
Let me know if anything else is required from my end to explain what exactly I have done and what I am trying to achieve.
There is not much await and async in your example so I've just addressed the counting part of your question in general terms in asyncio. asyncio.Queue is good for this because you can separate out the counting from the cause quite simply.
import asyncio
import aiohttp
class Count():
def __init__(self, queue: asyncio.Queue):
self.queue = queue
self.good = 0
self.bad = 0
async def count(self):
while True:
result = await self.queue.get()
if result == 'Exit':
return
if result == 200:
self.good += 1
else:
self.bad += 1
async def request(q: asyncio.Queue):
async with aiohttp.ClientSession() as session:
for _ in range(5): # just poll 30 times in this instance
await asyncio.sleep(0.1)
async with session.get(
'https://httpbin.org/status/200%2C500', ssl=False
) as response:
q.put_nowait(response.status)
q.put_nowait('Exit')
async def main():
q = asyncio.Queue()
cnt = Count(q)
tasks = [cnt.count(), request(q)]
await asyncio.gather(*[asyncio.create_task(t) for t in tasks])
print(cnt.good, cnt.bad)
if __name__ == "__main__":
asyncio.run(main())
Output is random given httpbin response. Should add to 5.
4 1

Store http and websoket updates in parallel

I want receive http updates and WS updates in parallel.
For this targets i use threading, but i still receive only updates from WS. It's looks like
def http_req_update is blocked by infiniti messages from def on_message
Could please someone help me with it ?
import json
import time
from websocket import WebSocketApp
import requests
import threading
class BithumbWebSocketApp(WebSocketApp):
def __init__(self, url, **kwargs):
super(BithumbWebSocketApp, self).__init__(url, **kwargs)
def _request_orderbookdepth(self, channel, event=None, payload=None, auth_required=True):
current_time = int(time.time())
data = {
"time": current_time,
"type": "orderbookdepth",
"symbols": ["BTC_KRW"],
}
data = json.dumps(data)
print('request1', data)
self.send(data)
def subscribe(self, channel, payload=None, auth_required=True):
self._request_orderbookdepth(channel, "subscribe", payload, auth_required)
def unsubscribe(self, channel, payload=None, auth_required=True):
self._request_orderbookdepth(channel, "unsubscribe", payload, auth_required)
def on_open(ws):
print('Connected')
ws.subscribe("wss://pubwss.bithumb.com/pub/ws", "BTC_KRW", False)
msg_lst = []
def on_message(ws, message):
print('message', message)
msg = json.loads(message.encode('utf-8'))
print('msg1: ', msg)
msg_lst.append({
"msg": msg,
"type": msg['type'],
"list": msg['content']['list'],
"datetime": msg['content']['datetime'],
})
lst_to_json = json.dumps(msg_lst)
def ws_update():
app = BithumbWebSocketApp("wss://pubwss.bithumb.com/pub/ws",
on_open=on_open,
on_message=on_message)
app.run_forever(ping_interval=5)
def http_req_update():
currency = 'BTC_KRW' # ALL
url = f"https://api.bithumb.com/public/orderbook/{currency}"
headers = {
"accept": "application/json",
"content-type": "application/json"
}
response = requests.get(url, headers=headers)
print('snapshot_response', response.text)
if __name__ == "__main__":
trd1 = threading.Thread(target=ws_update)
trd2 = threading.Thread(target=http_req_update)
trd1.start()
trd2.start()

Python passing arguments to function Flask

I am writing a Flask application script and have encountered a function that accepts arguments and functions outside of the flask app in a standalone script but will not accept the the 'TFEVT_node_id'argument I pass to it inside of the Flask app. Here is the Flask code:
#################################################
# Flask Setup
#################################################
#app = Flask(__name__, static_url_path='')
app = Flask(__name__)
CORS(app)
#################################################
#global variables
TFEVT_node_id = ''
token = ''
#################################################
# CityIQ API calls
#################################################
def get_token():
print("Get Token")
url = 'https://auth.aa.cityiq.io/oauth/token'
querystring = {"grant_type":"client_credentials"}
response = requests.get(url, auth=HTTPBasicAuth(client,secret), params=querystring).json()
token = response['access_token']
print("Token Received")
return token
#given a specific subasset this function returns the CityIQ traffic events for a given period of time in minutes
def get_traffic(TFEVT_node_id, lookback_minutes):
url_string = '/events'
url = "https://sandiego.cityiq.io/api/v2/event/assets/"+TFEVT_node_id+url_string
ts = datetime.datetime.now().timestamp()
CityIQ_Current_TS = int(ts*1000)
CityIQ_TS_Calc_TS = datetime.datetime.now() - timedelta(minutes=lookback_minutes)
CityIQ_Starttime_TS = int((ts-(lookback_minutes*60))*1000)
querystring = {"eventType":"TFEVT","startTime":CityIQ_Starttime_TS,"endTime":CityIQ_Current_TS,"pageSize":"100"}
payload = ""
headers = {
'Authorization': "Bearer {}".format(token),
'Predix-Zone-Id': "SD-IE-TRAFFIC",
'cache-control': "no-cache",
}
response = requests.request("GET", url, headers=headers, params=querystring).json()
return(response)
#give it an CityIQ node ID and it will return the asset TFEVT child .
def get_asset_TFEVT(node_id):
url = "https://sandiego.cityiq.io/api/v2/metadata/assets/"+node_id+"/subAssets"
payload = ""
headers = {
'Authorization': "Bearer {}".format(token),
'Predix-Zone-Id': "SD-IE-ENVIRONMENTAL",
'cache-control': "no-cache",
}
response = requests.request("GET", url, data=payload, headers=headers).json()
for i in response['assets']:
if any('TFEVT' in i for i in [i][0]['eventTypes']):
global TFEVT_node_id
TFEVT_node_id = ([i][0]['assetUid'])
return(TFEVT_node_id)
#test def that should be removed in production
def test(TFEVT_node_id, lookback_minutes):
found_cars = get_traffic(TFEVT_node_id, lookback_minutes)
print(found_cars)
return(found_cars)
#################################################
# Flask Routes
#################################################
#app.route('/')
def hello_world():
global token
token = get_token()
return 'documentation comming soon!'
#app.route('/test/<string:node_id>')
def go(node_id):
global token
token = get_token()
global TFEVT_node_id
TFEVT_node_id = get_asset_TFEVT(node_id)
cars_list = []
lookback_minutes = 15
env_output = {}
ccars = test(TFEVT_node_id, lookback_minutes)
cars_list.append(ccars)
env_output.update({'Cars' : cars_list})
if __name__ == '__main__':
app.run()
Again, I am getting the desired result when these functions are run outside of a Flask app. Inside of the Flask app (running the code above) returns a TypeError: The view function did not return a valid response. I have traced that back to the 404 response in the gettraffic function where the lookback minutes variable is getting through but the TFEVT_node_id isn't.
I am new to Python.
Your go() function does not have a return. A response is expected. See: About responses.

Python HTTP Post method returns response as magicmock object instead of value

I am trying to check the response status code after trigerring some API with a POST method, Response status code is of Magicmock instance type, i am checking whether the status code is inbetween 400 and 500 using comparison operator which works in python 2 but raises TypeError in python 3
import mock
response = <MagicMock name='Session().post()' id='130996186'>
Below code works in python 2
if (400 <= response.status_code <= 500):
print('works')
But when executed in python 3, raises
TypeError: '<=' not supported between instances of 'int' and 'MagicMock'
class BMRAPI(object):
root_url = None
def __init__(self, user, api_key, root_url=BMR_URL,
api_uri=RESULTS_API_URI):
self.log =
logging.getLogger("BMRframework.Reporting.BMR6.BMRAPI")
self.root_url = root_url
self.url = urljoin(root_url, api_uri)
self.log.info("Connecting to BMR REST API: %s" % self.url)
self.session = requests.Session()
auth = 'ApiKey {0}:{1}'.format(user, api_key)
self.session.headers.update({
'Content-type': 'application/json',
'Accept': 'text/plain',
'Authorization': auth})
self.session.trust_env = False # bypass the proxy
self.log.debug("Authenticating as: %s" % user)
self.log.debug("Using API Key: %s" % api_key)`enter code here`
self.log.info("Connection to REST API successful")
def url_for_resource(self, resource_name):
return urljoin(self.url, resource_name) + "/"
def create(self, resource_name, data):
response = self.session.post(self.url_for_resource(resource_name),
json.dumps(data), timeout=TIMEOUT)
return self.handle_response(response)
def handle_response(self, response):
if (400 <= response.status_code <= 500):
print('mars')
Below is the UNit test case
#mock.patch("requests.Session")
def BMRAPI(Session):
api = BMRAPI('http://1.2.3.4/', 'dummy_user', '12345')
data = {'hello': 123}
api.create('testresource', data)
This isn't exactly a fix, more of a workaround.
Instead of making that <= comparison, write a separate method:
def is_4xx_or_5xx_code(status_code):
return 400 <= status_code <= 500
if is_4xx_or_5xx_code(status_code=response.status_code):
print('works')
Then mock it in your tests.
#mock.patch('path.to_code.under_test.is_4xx_or_5xx_code')
def test_your_method(mock_status_code):
mock_status_code.return_value = True
# rest of the test.

Resources