364 lines
15 KiB
Python
364 lines
15 KiB
Python
|
import random
|
||
|
import re
|
||
|
import sys
|
||
|
import threading
|
||
|
import time
|
||
|
|
||
|
import TCPConnUtility
|
||
|
from NativeLog import NativeLog
|
||
|
from TCAction import TCActionBase
|
||
|
|
||
|
reload(sys)
|
||
|
sys.setdefaultencoding('iso-8859-1') # # use encoding that with 1 Byte length and contain 256 chars
|
||
|
|
||
|
|
||
|
DEFAULT_MAX_CONN_ALLOWED = 5
|
||
|
|
||
|
|
||
|
# complicated design because I want to make this script applied for all TCP connect/close test scenarios
|
||
|
# basic flow: try to create max connections, send/recv data if possible, close all connections
|
||
|
# connect:
|
||
|
# 1. find available (target_link_id, socket_id) list,
|
||
|
# notice that target_link_id maybe not correct if PC is client
|
||
|
# (during that time, some link may timeout and got disconnected from FIN_WAIT or other state)
|
||
|
# 2. choose one method from method set, try to connect
|
||
|
# 3. update state table: a)check result and destination state, b)find real target_link_id, c)update
|
||
|
# send/recv data:
|
||
|
# 1. find channels that are possible to send data on all connections
|
||
|
# 2. send data on possible channels
|
||
|
# disconnect:
|
||
|
# 1. find available connections
|
||
|
# 2. choose one method from disconnect set, try to disconnect
|
||
|
# 3. update state table (phase 1)
|
||
|
# async process:
|
||
|
# listen on AT UART port, record all "x,CONNECT" and "x,CLOSE" command
|
||
|
# for "x,CONNECT", append them to self.target_link_id_list, used when need to find real target_link_id
|
||
|
# for "x,CLOSE", update state table (phase 2), if matching connection is pending on wait state,
|
||
|
# close them and remove from state table
|
||
|
class TCPConnStressTC(TCActionBase.CommonTCActionBase):
|
||
|
|
||
|
def __init__(self, name, test_env, cmd_set, timeout=30, log_path=TCActionBase.LOG_PATH):
|
||
|
TCActionBase.CommonTCActionBase.__init__(self, name, test_env, cmd_set=cmd_set,
|
||
|
timeout=timeout, log_path=log_path)
|
||
|
self.__at1_buff = ""
|
||
|
self.max_conn_allowed = test_env.get_variable_by_name("max_conn")
|
||
|
self.enable_log = True
|
||
|
# load param from excel
|
||
|
for i in range(1, len(cmd_set)):
|
||
|
if cmd_set[i][0] != "dummy":
|
||
|
cmd_string = "self." + cmd_set[i][0]
|
||
|
exec cmd_string
|
||
|
|
||
|
# connection_state_dict: {target_link_id: [socket_id, target_state, socket_state, is_closed]}
|
||
|
# is_closed: found "x,CLOSE" in AT UART port
|
||
|
self.connection_state_dict = dict(zip(range(self.max_conn_allowed), [None] * self.max_conn_allowed))
|
||
|
self.created_link_id_list = []
|
||
|
|
||
|
self.__available_soc_id = range(2, 2+self.max_conn_allowed)
|
||
|
self.__available_link_id = range(self.max_conn_allowed)
|
||
|
|
||
|
self.result_cntx = TCActionBase.ResultCheckContext(self, test_env, self.tc_name)
|
||
|
self.utility = TCPConnUtility.TCPConnUtility(self)
|
||
|
self.state_lock = threading.Lock()
|
||
|
self.link_id_lock = threading.Lock()
|
||
|
self.available_id_lock = threading.Lock()
|
||
|
pass
|
||
|
|
||
|
def __add_log(self, log_str):
|
||
|
if self.enable_log is True:
|
||
|
NativeLog.add_trace_info(log_str)
|
||
|
|
||
|
def __get_created_target_link_id(self):
|
||
|
self.link_id_lock.acquire()
|
||
|
try:
|
||
|
link_id = self.created_link_id_list[-1]
|
||
|
self.created_link_id_list = []
|
||
|
finally:
|
||
|
self.link_id_lock.release()
|
||
|
return link_id
|
||
|
pass
|
||
|
|
||
|
def __set_created_target_link_id(self, link_id):
|
||
|
self.link_id_lock.acquire()
|
||
|
try:
|
||
|
self.created_link_id_list.append(link_id)
|
||
|
finally:
|
||
|
self.link_id_lock.release()
|
||
|
pass
|
||
|
|
||
|
def __find_channel_list(self):
|
||
|
channel_list = [] # # [(socket_id, able_to_send, link_id, able_to_send), ]
|
||
|
self.state_lock.acquire()
|
||
|
try:
|
||
|
for link_id in self.connection_state_dict:
|
||
|
state = self.connection_state_dict[link_id]
|
||
|
if state is not None:
|
||
|
channel_list.append([state[0], self.utility.is_able_to_send_data(state[2]),
|
||
|
link_id, self.utility.is_able_to_send_data(state[1])])
|
||
|
finally:
|
||
|
self.state_lock.release()
|
||
|
return channel_list
|
||
|
pass
|
||
|
|
||
|
def __established_connection_list(self):
|
||
|
conn_list = [] # # [(socket_id, link_id), ]
|
||
|
self.state_lock.acquire()
|
||
|
try:
|
||
|
for link_id in self.connection_state_dict:
|
||
|
state = self.connection_state_dict[link_id]
|
||
|
if state is not None:
|
||
|
if self.utility.is_established_connection([state[1], state[2]]) is True:
|
||
|
conn_list.append([state[0], link_id])
|
||
|
finally:
|
||
|
self.state_lock.release()
|
||
|
return conn_list
|
||
|
pass
|
||
|
|
||
|
# find free socket_id, target_link_id pair
|
||
|
def __get_available_id_list(self):
|
||
|
self.available_id_lock.acquire()
|
||
|
try:
|
||
|
id_list = zip(self.__available_soc_id, self.__available_link_id)
|
||
|
finally:
|
||
|
self.available_id_lock.release()
|
||
|
return id_list
|
||
|
pass
|
||
|
|
||
|
def __update_available_id_list(self, soc_id, link_id, action="ADD"):
|
||
|
self.available_id_lock.acquire()
|
||
|
try:
|
||
|
if action == "ADD":
|
||
|
self.__available_link_id.append(link_id)
|
||
|
self.__available_soc_id.append(soc_id)
|
||
|
self.__add_log("[AVAILABLE ID]soc %d link %d is available" % (soc_id, link_id))
|
||
|
elif action == "REMOVE":
|
||
|
self.__available_link_id.remove(link_id)
|
||
|
self.__available_soc_id.remove(soc_id)
|
||
|
self.__add_log("[AVAILABLE ID]soc %d link %d is used" % (soc_id, link_id))
|
||
|
finally:
|
||
|
self.available_id_lock.release()
|
||
|
|
||
|
def __update_connection_state_item(self, target_link_id, socket_id=None,
|
||
|
target_state=None, socket_state=None, is_closed=None):
|
||
|
self.state_lock.acquire()
|
||
|
try:
|
||
|
state = self.connection_state_dict[target_link_id]
|
||
|
if state is None:
|
||
|
state = [None] * 4
|
||
|
if socket_id is not None:
|
||
|
state[0] = socket_id
|
||
|
if target_state is not None:
|
||
|
state[1] = target_state
|
||
|
if socket_state is not None:
|
||
|
state[2] = socket_state
|
||
|
if is_closed is not None:
|
||
|
state[3] = is_closed
|
||
|
|
||
|
# remove closed connections
|
||
|
closed = self.utility.is_closed_state(state[1]) and (state[3] is True)
|
||
|
if closed is True:
|
||
|
self.__update_available_id_list(state[0], target_link_id)
|
||
|
state = None
|
||
|
# if new connection created
|
||
|
if self.connection_state_dict[target_link_id] is None:
|
||
|
created = self.utility.is_created_state(state[1])
|
||
|
if created is True:
|
||
|
self.__update_available_id_list(state[0], target_link_id, "REMOVE")
|
||
|
else:
|
||
|
# connection did not created, do not add them to connection state table
|
||
|
state = None
|
||
|
|
||
|
# set new connection_state
|
||
|
self.connection_state_dict[target_link_id] = state
|
||
|
self.__add_log("[STATE] link id is %d, state is %s" % (target_link_id, state))
|
||
|
except StandardError, e:
|
||
|
pass
|
||
|
finally:
|
||
|
self.state_lock.release()
|
||
|
pass
|
||
|
|
||
|
# update state table: if result is false, return, if result is true:
|
||
|
# for connect, find real link id, update table according to destination state
|
||
|
# for disconnect, if target in SOC_CLOSE_STATE && catch "x,CLOSE" from AT, remove the item
|
||
|
def update_connection_state_table(self, conn_id, destination_state=None):
|
||
|
if isinstance(conn_id, list) is True or isinstance(conn_id, tuple) is True:
|
||
|
socket_id = conn_id[0]
|
||
|
try:
|
||
|
target_link_id = self.__get_created_target_link_id()
|
||
|
except IndexError:
|
||
|
target_link_id = conn_id[1]
|
||
|
self.__add_log("[STATE]fail to get link id, state is %s, %s"
|
||
|
% (destination_state[0], destination_state[1]))
|
||
|
self.__update_connection_state_item(target_link_id, socket_id,
|
||
|
destination_state[0], destination_state[1])
|
||
|
pass
|
||
|
else: # # called when recv CLOSED
|
||
|
target_link_id = conn_id
|
||
|
self.__update_connection_state_item(target_link_id, is_closed=True)
|
||
|
pass
|
||
|
pass
|
||
|
|
||
|
def process_at_data(self, data):
|
||
|
pos1 = 0
|
||
|
pos2 = 0
|
||
|
connect = re.compile("\d,CONNECT\r\n")
|
||
|
close = re.compile("\d,CLOSED\r\n")
|
||
|
connect_match = connect.findall(data)
|
||
|
close_match = close.findall(data)
|
||
|
close = re.compile("\d,CONNECT FAIL\r\n")
|
||
|
close_match += close.findall(data)
|
||
|
if len(connect_match) != 0:
|
||
|
pos1 = data.find(connect_match[-1]) + 9
|
||
|
# append last connected link id
|
||
|
self.__set_created_target_link_id(int(connect_match[-1][:1]))
|
||
|
pass
|
||
|
if len(close_match) != 0:
|
||
|
pos2 = data.find(close_match[-1]) + 7
|
||
|
# update for all closed links
|
||
|
for close_str in close_match:
|
||
|
self.update_connection_state_table(int(close_str[:1]))
|
||
|
pass
|
||
|
pos = pos1 if pos1 > pos2 else pos2
|
||
|
|
||
|
return data[pos:]
|
||
|
|
||
|
def execute(self):
|
||
|
TCActionBase.TCActionBase.execute(self)
|
||
|
self.result_cntx.start()
|
||
|
|
||
|
# configurable params
|
||
|
# mandatory params
|
||
|
try:
|
||
|
connect_method_set = self.connect_method_set
|
||
|
test_count = self.test_count
|
||
|
except StandardError, e:
|
||
|
NativeLog.add_trace_critical("Error configuration for TCPConnSingleMode script, error is %s" % e)
|
||
|
raise StandardError("Error configuration")
|
||
|
# optional params
|
||
|
try:
|
||
|
disconn_method_set = self.disconn_method_set
|
||
|
except StandardError:
|
||
|
disconn_method_set = ["D_05"]
|
||
|
pass
|
||
|
try:
|
||
|
delay = self.delay
|
||
|
except StandardError:
|
||
|
delay = 0
|
||
|
pass
|
||
|
try:
|
||
|
check_data_len = self.check_data_len
|
||
|
except StandardError:
|
||
|
check_data_len = [0, 0]
|
||
|
pass
|
||
|
if isinstance(check_data_len, list) is False:
|
||
|
check_data_len = [check_data_len] * 2
|
||
|
# configurable params
|
||
|
|
||
|
# step1 use <test_tcp_port1> to create server on both PC and target side
|
||
|
checker_stings = ["SOCP SOC_COM L OK", "ATP AT1 L OK"]
|
||
|
test_action_string = ["SOC SOC1 LISTEN <test_tcp_port1>", "ATC AT1 CIPSERVER 1 <test_tcp_port1>"]
|
||
|
fail_string = "Fail, Fail on create server"
|
||
|
if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False:
|
||
|
return
|
||
|
|
||
|
for tested_count in xrange(test_count):
|
||
|
# step2 do connect
|
||
|
available_id_list = self.__get_available_id_list()
|
||
|
|
||
|
for conn_id in available_id_list:
|
||
|
connect_method = random.choice(connect_method_set)
|
||
|
# ret, destination_state = self.utility.execute_tcp_method(connect_method, conn_id)
|
||
|
try:
|
||
|
self.__add_log("[ACTION]connect method is %s, connect id is %s"
|
||
|
% (connect_method, conn_id))
|
||
|
ret, destination_state = self.utility.execute_tcp_method(connect_method, conn_id)
|
||
|
except StandardError, e:
|
||
|
NativeLog.add_trace_critical("Error in connect, error is %s" % e)
|
||
|
raise StandardError("Exception happen when connect")
|
||
|
if ret is False:
|
||
|
# connect fail, should terminate TC and mark as fail
|
||
|
return
|
||
|
else:
|
||
|
# succeed, append to table
|
||
|
self.update_connection_state_table(conn_id, destination_state)
|
||
|
if delay != 0:
|
||
|
time.sleep(delay)
|
||
|
|
||
|
# step 3 send/recv test data
|
||
|
# # [(socket_id, able_to_send, link_id, able_to_send)]
|
||
|
self.__add_log("[ACTION]SEND/RECV data")
|
||
|
channel_list = self.__find_channel_list()
|
||
|
for channel in channel_list:
|
||
|
_check_data_len = [0, 0]
|
||
|
if channel[1] is True:
|
||
|
_check_data_len[0] = check_data_len[0]
|
||
|
if channel[3] is True:
|
||
|
_check_data_len[1] = check_data_len[1]
|
||
|
ret = self.utility.send_test_data(channel[0],
|
||
|
channel[2],
|
||
|
_check_data_len)
|
||
|
if ret is False:
|
||
|
# send/recv fail, should terminate TC and mark as fail
|
||
|
return
|
||
|
if delay != 0:
|
||
|
time.sleep(delay)
|
||
|
|
||
|
# step 4 close all established connections
|
||
|
# (socket_id, link_id)
|
||
|
conn_list = self.__established_connection_list()
|
||
|
for conn_id in conn_list:
|
||
|
disconn_method = random.choice(disconn_method_set)
|
||
|
try:
|
||
|
self.__add_log("[ACTION]disconnect method is %s, connect id is %s"
|
||
|
% (disconn_method, conn_id))
|
||
|
ret, destination_state = self.utility.execute_tcp_method(disconn_method, conn_id)
|
||
|
except StandardError, e:
|
||
|
NativeLog.add_trace_critical("Error in disconnect, error is %s" % e)
|
||
|
raise StandardError("Exception happen when disconnect")
|
||
|
if ret is False:
|
||
|
# connect fail, should terminate TC and mark as fail
|
||
|
return
|
||
|
else:
|
||
|
# succeed, append to table
|
||
|
self.update_connection_state_table(conn_id, destination_state)
|
||
|
if delay != 0:
|
||
|
time.sleep(delay)
|
||
|
|
||
|
# finally, execute done
|
||
|
self.result_cntx.set_result("Succeed")
|
||
|
|
||
|
def result_check(self, port_name, data):
|
||
|
TCActionBase.CommonTCActionBase.result_check(self, port_name, data)
|
||
|
self.result_cntx.append_data(port_name, data)
|
||
|
# find "x,CONNECT" and "x,CLOSE"
|
||
|
if port_name.find("AT") != -1:
|
||
|
self.__at1_buff += data
|
||
|
self.__at1_buff = self.process_at_data(self.__at1_buff)
|
||
|
|
||
|
|
||
|
def main():
|
||
|
at1_buff = ""
|
||
|
pos1 = 0
|
||
|
pos2 = 0
|
||
|
data = "dafgajglajdfg0,CLOSEjdalghalksdg1,CONNECT\r\n\r\n3,CONNECT4,CLOSEadfaasdf"
|
||
|
at1_buff += data
|
||
|
connect = re.compile("\d,CONNECT")
|
||
|
close = re.compile("\d,CLOSE")
|
||
|
connect_match = connect.findall(at1_buff)
|
||
|
close_match = close.findall(at1_buff)
|
||
|
if len(connect_match) != 0:
|
||
|
pos1 = at1_buff.find(connect_match[-1]) + 9
|
||
|
pass
|
||
|
if len(close_match) != 0:
|
||
|
pos2 = at1_buff.find(close_match[-1]) + 7
|
||
|
pass
|
||
|
pos = pos1 if pos1 > pos2 else pos2
|
||
|
|
||
|
at1_buff = at1_buff[pos:]
|
||
|
|
||
|
pass
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
main()
|
||
|
|