X-Git-Url: https://gerrit.fd.io/r/gitweb?p=csit.git;a=blobdiff_plain;f=resources%2Ftools%2Fwrk%2Fwrk_traffic_profile_parser.py;fp=resources%2Ftools%2Fwrk%2Fwrk_traffic_profile_parser.py;h=0000000000000000000000000000000000000000;hp=f5534657055a1b49afee05aec5bd8db270b2c1bf;hb=80adeefab9fc1871675688881498304ddc780828;hpb=f9b18c597f8bd9419ce3ecdd36de351f7b05c59a diff --git a/resources/tools/wrk/wrk_traffic_profile_parser.py b/resources/tools/wrk/wrk_traffic_profile_parser.py deleted file mode 100644 index f553465705..0000000000 --- a/resources/tools/wrk/wrk_traffic_profile_parser.py +++ /dev/null @@ -1,294 +0,0 @@ -# Copyright (c) 2019 Cisco and / or its affiliates. -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy -# of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions -# and limitations under the License. - -"""wrk traffic profile parser. - -See LLD for the structure of a wrk traffic profile. -""" - - -from os.path import isfile -from pprint import pformat - -from yaml import safe_load, YAMLError -from robot.api import logger - -from resources.tools.wrk.wrk_errors import WrkError - - -class WrkTrafficProfile: - """The wrk traffic profile. - """ - - MANDATORY_PARAMS = ( - u"urls", - u"first-cpu", - u"cpus", - u"duration", - u"nr-of-threads", - u"nr-of-connections" - ) - - INTEGER_PARAMS = ( - (u"cpus", 1), - (u"first-cpu", 0), - (u"duration", 1), - (u"nr-of-threads", 1), - (u"nr-of-connections", 1) - ) - - def __init__(self, profile_name): - """Read the traffic profile from the yaml file. - - :param profile_name: Path to the yaml file with the profile. - :type profile_name: str - :raises: WrkError if it is not possible to parse the profile. - """ - - self._profile_name = None - self._traffic_profile = None - - self.profile_name = profile_name - - try: - with open(self.profile_name, u"rt") as profile_file: - self.traffic_profile = safe_load(profile_file) - except IOError as err: - raise WrkError( - msg=f"An error occurred while opening the file " - f"'{self.profile_name}'.", details=str(err) - ) - except YAMLError as err: - raise WrkError( - msg=f"An error occurred while parsing the traffic profile " - f"'{self.profile_name}'.", details=str(err) - ) - - self._validate_traffic_profile() - - if self.traffic_profile: - logger.debug( - f"\nThe wrk traffic profile '{self.profile_name}' is valid.\n" - ) - logger.debug(f"wrk traffic profile '{self.profile_name}':") - logger.debug(pformat(self.traffic_profile)) - else: - logger.debug( - f"\nThe wrk traffic profile '{self.profile_name}' is invalid.\n" - ) - raise WrkError( - f"\nThe wrk traffic profile '{self.profile_name}' is invalid.\n" - ) - - def __repr__(self): - return pformat(self.traffic_profile) - - def __str__(self): - return pformat(self.traffic_profile) - - def _validate_traffic_profile(self): - """Validate the traffic profile. - - The specification, the structure and the rules are described in - doc/wrk_lld.rst - """ - - logger.debug( - f"\nValidating the wrk traffic profile '{self.profile_name}'...\n" - ) - if not (self._validate_mandatory_structure() - and self._validate_mandatory_values() - and self._validate_optional_values() - and self._validate_dependencies()): - self.traffic_profile = None - - def _validate_mandatory_structure(self): - """Validate presence of mandatory parameters in trafic profile dict - - :returns: whether mandatory structure is followed by the profile - :rtype: bool - """ - # Level 1: Check if the profile is a dictionary: - if not isinstance(self.traffic_profile, dict): - logger.error(u"The wrk traffic profile must be a dictionary.") - return False - - # Level 2: Check if all mandatory parameters are present: - is_valid = True - for param in self.MANDATORY_PARAMS: - if self.traffic_profile.get(param, None) is None: - logger.error(f"The parameter '{param}' in mandatory.") - is_valid = False - return is_valid - - def _validate_mandatory_values(self): - """Validate that mandatory profile values satisfy their constraints - - :returns: whether mandatory values are acceptable - :rtype: bool - """ - # Level 3: Mandatory params: Check if urls is a list: - is_valid = True - if not isinstance(self.traffic_profile[u"urls"], list): - logger.error(u"The parameter 'urls' must be a list.") - is_valid = False - - # Level 3: Mandatory params: Check if integers are not below minimum - for param, minimum in self.INTEGER_PARAMS: - if not self._validate_int_param(param, minimum): - is_valid = False - return is_valid - - def _validate_optional_values(self): - """Validate values for optional parameters, if present - - :returns: whether present optional values are acceptable - :rtype: bool - """ - is_valid = True - # Level 4: Optional params: Check if script is present: - script = self.traffic_profile.get(u"script", None) - if script is not None: - if not isinstance(script, str): - logger.error(u"The path to LuaJIT script in invalid") - is_valid = False - else: - if not isfile(script): - logger.error(f"The file '{script}' does not exist.") - is_valid = False - else: - self.traffic_profile[u"script"] = None - logger.debug( - u"The optional parameter 'LuaJIT script' is not defined. " - u"No problem." - ) - - # Level 4: Optional params: Check if header is present: - header = self.traffic_profile.get(u"header", None) - if header is not None: - if isinstance(header, dict): - header = u", ".join( - f"{0}: {1}".format(*item) for item in header.items() - ) - self.traffic_profile[u"header"] = header - elif not isinstance(header, str): - logger.error(u"The parameter 'header' type is not valid.") - is_valid = False - - if not header: - logger.error(u"The parameter 'header' is defined but empty.") - is_valid = False - else: - self.traffic_profile[u"header"] = None - logger.debug( - u"The optional parameter 'header' is not defined. No problem." - ) - - # Level 4: Optional params: Check if latency is present: - latency = self.traffic_profile.get(u"latency", None) - if latency is not None: - if not isinstance(latency, bool): - logger.error(u"The parameter 'latency' must be boolean.") - is_valid = False - else: - self.traffic_profile[u"latency"] = False - logger.debug( - u"The optional parameter 'latency' is not defined. No problem." - ) - - # Level 4: Optional params: Check if timeout is present: - if u"timeout" in self.traffic_profile: - if not self._validate_int_param(u"timeout", 1): - is_valid = False - else: - self.traffic_profile[u"timeout"] = None - logger.debug( - u"The optional parameter 'timeout' is not defined. No problem." - ) - - return is_valid - - def _validate_dependencies(self): - """Validate dependencies between parameters - - :returns: whether dependencies between parameters are acceptable - :rtype: bool - """ - # Level 5: Check urls and cpus: - if self.traffic_profile[u"cpus"] % len(self.traffic_profile[u"urls"]): - logger.error( - u"The number of CPUs must be a multiple of the number of URLs." - ) - return False - return True - - def _validate_int_param(self, param, minimum): - """Validate that an int parameter is set acceptably - If it is not an int already but a string, convert and store it as int. - - :param param: Name of a traffic profile parameter - :param minimum: The minimum value for the named parameter - :type param: str - :type minimum: int - :returns: whether param is set to an int of at least minimum value - :rtype: bool - """ - value = self._traffic_profile[param] - if isinstance(value, str): - if value.isdigit(): - value = int(value) - else: - value = minimum - 1 - if isinstance(value, int) and value >= minimum: - self.traffic_profile[param] = value - return True - logger.error( - f"The parameter '{param}' must be an integer and at least {minimum}" - ) - return False - - @property - def profile_name(self): - """Getter - Profile name. - - :returns: The traffic profile file path - :rtype: str - """ - return self._profile_name - - @profile_name.setter - def profile_name(self, profile_name): - """ - - :param profile_name: - :type profile_name: str - """ - self._profile_name = profile_name - - @property - def traffic_profile(self): - """Getter: Traffic profile. - - :returns: The traffic profile. - :rtype: dict - """ - return self._traffic_profile - - @traffic_profile.setter - def traffic_profile(self, profile): - """Setter - Traffic profile. - - :param profile: The new traffic profile. - :type profile: dict - """ - self._traffic_profile = profile