From 8937d211d6c72469b0dcc202daa8fb53645906c2 Mon Sep 17 00:00:00 2001 From: fumingwei Date: Tue, 21 May 2024 10:07:51 +0800 Subject: [PATCH] feature: Support read service function maps. --- images_build/client/Dockerfile | 2 + images_build/client/dign_client/bin/client.py | 130 +++++++++++++----- .../client/dign_client/etc/client.conf | 7 - 3 files changed, 95 insertions(+), 44 deletions(-) diff --git a/images_build/client/Dockerfile b/images_build/client/Dockerfile index 0e23611..640e20e 100644 --- a/images_build/client/Dockerfile +++ b/images_build/client/Dockerfile @@ -12,6 +12,8 @@ RUN sed -i s@/dl-cdn.alpinelinux.org/@/mirrors.ustc.edu.cn/@g /etc/apk/repositor && pip3 install CIUnitTest \ && pip3 install pytelegraf \ && pip3 install dnspython \ + && pip3 install prettytable \ + && pip3 install pyyaml \ && mv /opt/dign_client/etc/client.conf /opt/dign_client/etc/client.conf.sample WORKDIR /opt/dign_client diff --git a/images_build/client/dign_client/bin/client.py b/images_build/client/dign_client/bin/client.py index f06bf8b..b302fc5 100644 --- a/images_build/client/dign_client/bin/client.py +++ b/images_build/client/dign_client/bin/client.py @@ -19,6 +19,7 @@ import sys import logging import copy from prettytable import PrettyTable,NONE,HEADER +import yaml class ConfigLoader: DEFAULT_CONFIGS = { @@ -162,9 +163,6 @@ class ConfigLoader: }, 'test_shaping_ratelimit_1000gbps_https': { 'conn_timeout': 4,'max_recv_speed_large': 6553600 - }, - 'start_time_random_delay_range': { - 'left_edge': 1,'right_edge': 30 } } @@ -192,6 +190,45 @@ class ConfigLoader: else: raise KeyError("%s not in configs or %s.%s not in configs." % (section, section, key)) +class ServiceFunctionConfigLoader: + TABLE_NAME = "service_function_maps" + SF_NAME_KEY = "service_function_name" + DEVICE_ID_KEY = "device_id" + + def __init__(self, config_path, name_pattern): + self._config_path = config_path + self._name_pattern = name_pattern + self._configs = None + self._load_configs() + self._pairs = self._read_name_id_pairs_by_name_pattern(self._name_pattern) + + def _load_configs(self): + try: + with open(self._config_path, 'r') as f: + load_configs = yaml.safe_load(f) + if load_configs is not None: + self._configs = load_configs + else: + print(f"Error: No config loaded from path: {self._config_path}") + except FileNotFoundError: + print(f"Error: File not found: {self._config_path}") + except yaml.YAMLError as e: + print(f"Error parsing YAML file: {e}") + + def _read_name_id_pairs_by_name_pattern(self, name_pattern): + if (self._configs is None) or (self.TABLE_NAME not in self._configs): + return [] + + return [ + {"name": item[self.SF_NAME_KEY], "id": int(item[self.DEVICE_ID_KEY])} + for item in self._configs[self.TABLE_NAME] + if re.match(name_pattern, item[self.SF_NAME_KEY]) + ] + + @property + def name_id_pairs(self): + return self._pairs + class CommandParser: COUNT_MIN = 1 COUNT_MAX = 65535 @@ -209,12 +246,20 @@ class CommandParser: help='Specifies the number of TSG diagnoses. Default: 1. Range: [1,65535].') parser.add_argument('-l','--loop', action='store_true', default = False, help='Enable TSG diagnose loop, exit when recv a signal.') - parser.add_argument('--service_function_indexs', type = str, default = "0", - help = "Specifies the service function indexs. Example: 0,2,4-6,7. Range: [0,63]") + parser.add_argument('--service_function_names_regexp', type = str, default = '.+', + help = "Regexp of the case names to run. Example: .+") + parser.add_argument('--service_function_config_path', type = str, default = '/opt/dign_client/share/service_function_maps.yaml', + help = "Specifies the service function config file, default /opt/dign_client/share/service_function_maps.yaml") parser.add_argument('--config_path', type = str, default = '/opt/dign_client/etc/client.conf', help='Specifies the config file, default /opt/dign_client/etc/client.conf') parser.add_argument('--case_names_regexp', type = str, default = '.+', help='Regexp of the case names to run. Example: .+') + parser.add_argument('--enable_delay', action='store_true', default = False, + help='Enable a random delay between 1 and 30 seconds before running a case.') + parser.add_argument('--delay_left_edge', type = int, default = 1, + help='The seconds of the delay left edge. Default: 1.') + parser.add_argument('--delay_right_edge', type = int, default = 30, + help='The seconds of the delay right edge. Default: 30.') # parser.add_argument('-o','--compatible_mode', action='store_true', default = False, help='Tsg diagnose compatible mode to running') args = parser.parse_args() @@ -226,23 +271,21 @@ class CommandParser: self.__interval_s = args.interval self.__count = args.count self.__loop = args.loop - self.__service_function_indexs = args.service_function_indexs self.__config_path = args.config_path self.__case_names_regexp = args.case_names_regexp + self.__service_function_names_regexp = args.service_function_names_regexp + self.__service_function_config_path = args.service_function_config_path + self._enable_delay = args.enable_delay + self._delay_left_edge = args.delay_left_edge + self._delay_right_edge = args.delay_right_edge def __is_legal_args(self, args) -> bool: if args.count < self.COUNT_MIN or args.count > self.COUNT_MAX: print("Error: the number of TSG diagnoses must in [1,65535]") return False - str_indexs = args.service_function_indexs.split(",") - for item in str_indexs: - if not re.match(self.INDEX_PATTERN, item): - sub_indexs = item.split("-") - if len(sub_indexs) != 2 or (not re.match(self.INDEX_PATTERN, sub_indexs[0])) or \ - (not re.match(self.INDEX_PATTERN, sub_indexs[1])) or (int(sub_indexs[1]) < int(sub_indexs[0])): - print("Error: The service function indexs must in [0,63]") - return False + if args.delay_left_edge > args.delay_right_edge: + print("Error: The delay in seconds on the left edge is greater than on the right edge.") return True @@ -257,22 +300,6 @@ class CommandParser: @property def loop(self): return self.__loop - - @property - def service_function_indexs(self): - return self.__parse_service_function_indexs_str(self.__service_function_indexs) - - def __parse_service_function_indexs_str(self, indexs_str: str) -> list: - indexs = [] - str_indexs = indexs_str.split(",") - for item in str_indexs: - if re.match(self.INDEX_PATTERN, item): - indexs.append(int(item)) - else: - sub_str_indexs = item.split("-") - indexs.extend([i for i in range(int(sub_str_indexs[0]), int(sub_str_indexs[1]) + 1)]) - indexs = list(set(indexs)) - return indexs @property def config_path(self): @@ -282,6 +309,21 @@ class CommandParser: def case_names_regexp(self): return r"{}".format(self.__case_names_regexp) + @property + def service_function_names_regexp(self): + return r"{}".format(self.__service_function_names_regexp) + + @property + def service_function_config_path(self): + return self.__service_function_config_path + + @property + def delay_seconds(self): + if self._enable_delay: + return random.randint(self._delay_left_edge, self._delay_right_edge) + else: + return None + class ServerAddressBuilder: IPv4_4TH_OCTET_LEFT_EDGE = 101 DOMAIN_TO_PORT_LIST = [ @@ -355,9 +397,10 @@ class URLTransferBuilder: try: self._setup_connection() self._perform_connection() - self._close_connection() except pycurl.error as error_info: self._error_info = error_info + finally: + self._close_connection() @property def response_code(self): @@ -519,7 +562,7 @@ class DNSResponseAnalyzer: def is_error_type_equal(self, present_error_info, desired_type): if present_error_info is None: - return False, f"Error: The error info is None. Maybe the relevant actions didn’t take effect." + return False, f"Error: The error info is None. Maybe the relevant actions didn't take effect." if type(present_error_info) == desired_type: return True, None @@ -764,6 +807,9 @@ class ShapingCaseRunner: is_error_none = self._analyzer.is_pycurl_error_none(conn.error_info) if not is_error_none[0]: return False, is_error_none[1] + is_code_equal = self._analyzer.is_response_code_equal(conn.response_code, 200) + if not is_code_equal[0]: + return False, is_code_equal[1] return True, None def rate_limit_1000gbps_protocol_https(self, url, resolves, conn_timeout, max_recv_speed_large): @@ -772,6 +818,9 @@ class ShapingCaseRunner: is_error_none = self._analyzer.is_pycurl_error_none(conn.error_info) if not is_error_none[0]: return False, is_error_none[1] + is_code_equal = self._analyzer.is_response_code_equal(conn.response_code, 200) + if not is_code_equal[0]: + return False, is_code_equal[1] is_cert_matched = self._analyzer.is_cert_issuer_matched(conn.cert_issuer, r'\bCN[\s]*=[\s]*BadSSL\b') if not is_cert_matched[0]: return False, is_cert_matched[1] @@ -1063,9 +1112,12 @@ class DiagnoseCasesRunner: self._loop = cmd_parser.loop self._count = cmd_parser.count self._interval_s = cmd_parser.interval_s - self._service_function_indexs = cmd_parser.service_function_indexs self._case_names_regexp = cmd_parser.case_names_regexp self._config_path = cmd_parser.config_path + self._service_function_names_regexp = cmd_parser.service_function_names_regexp + self._service_function_config_path = cmd_parser.service_function_config_path + self._random_delay_seconds = cmd_parser.delay_seconds + self._config_loader = ConfigLoader(self._config_path) self._proxy_case = ProxyCasesRunner() @@ -1339,6 +1391,9 @@ class DiagnoseCasesRunner: ] def run_cases(self): + if self._random_delay_seconds is not None: + time.sleep(self._random_delay_seconds) + run_count = 1 while True: print("\nRUN %d" % run_count) @@ -1349,14 +1404,15 @@ class DiagnoseCasesRunner: run_count = run_count + 1 def __run_cases_by_service_function_ids(self): - for sf_id in self._service_function_indexs: + sf_loader = ServiceFunctionConfigLoader(self._service_function_config_path, self._service_function_names_regexp) + for sf_pair in sf_loader.name_id_pairs: start_timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) - print(format(("Service function id:" + str(sf_id) + ",Test start time: " + start_timestamp),'#^70s')) + print(format(("Service function name: " + str(sf_pair["name"]) + ",Test start time: " + start_timestamp),'#^100s')) - self._run_cases_in_one_service_function_id(sf_id) + self._run_cases_in_one_service_function_id(sf_pair["id"]) end_timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) - print(format(("Service function id:" + str(sf_id) + ",Test end time: " + end_timestamp),'=^70s')) + print(format(("Service function name: " + str(sf_pair["name"]) + ",Test end time: " + end_timestamp),'=^100s')) def _run_cases_in_one_service_function_id(self, service_function_id): resolve_Builder = ServerAddressBuilder(service_function_id) diff --git a/images_build/client/dign_client/etc/client.conf b/images_build/client/dign_client/etc/client.conf index c4de387..052f2de 100644 --- a/images_build/client/dign_client/etc/client.conf +++ b/images_build/client/dign_client/etc/client.conf @@ -236,10 +236,3 @@ max_recv_speed_large = 6553600 enabled = 1 conn_timeout = 4 max_recv_speed_large = 6553600 - -[start_time_random_delay_range] -enabled = 0 -#Left_edge is the left edge of the randomly generated time in seconds -left_edge = 0 -#Left_edge is the right edge of the randomly generated time in seconds -right_edge = 30