2 # Copyright 2021 Orange
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 # not use this file except in compliance with the License. You may obtain
6 # a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations
22 def __init__(self, testapi_url: str, logger):
25 testapi_url: testapi URL as a string, for instance
26 "http://172.20.73.203:8000/api/v1/results"
28 logger: reference to behave_tests logger.
31 self._base_url = testapi_url
34 def find_last_result(self, testapi_params, scenario_tag: str, nfvbench_test_input):
35 """Search testapi database and return latest result matching filters.
37 Look for the most recent testapi result matching testapi params, behave
38 scenario tag and nfvbench test input params, and return that result as a
42 testapi_params: dict holding the parameters of the testapi request. See
43 `build_testapi_url()` for the list of supported keys.
45 scenario_tag: Behave scenario tag to filter results. One of
46 "throughput" or "latency".
48 nfvbench_test_input: dict holding nfvbench test parameters and used
49 to filter the testapi results. The following keys are currently
51 - mandatory keys: 'duration_sec', 'frame_sizes', 'flow_count', 'rate'
52 - optional keys: 'user_label', 'flavor_type'
55 None if no result matching the filters can be found, else a dictionary
56 built from testapi JSON test result.
59 self._logger.info(f"find_last_result: filter on scenario tag: {scenario_tag}")
60 nfvbench_input_str = nfvbench_input_to_str(nfvbench_test_input)
61 self._logger.info(f"find_last_result: filter on test conditions: {nfvbench_input_str}")
64 while True: # While there are results pages to read
65 url = self._build_testapi_url(testapi_params, page)
66 self._logger.info("find_last_result: GET " + url)
67 last_results = self._do_testapi_request(url)
69 for result in last_results["results"]:
70 for tagged_result in result["details"]["results"][scenario_tag]:
71 if tagged_result["output"]["status"] != "OK":
72 # Drop result if nfvbench status is not OK
73 # (such result should not have been put in database by behave_tests,
74 # but let's be cautious)
76 if equal_test_conditions(tagged_result["input"], nfvbench_test_input):
79 if page >= last_results["pagination"]["total_pages"]:
85 def _build_testapi_url(self, testapi_params, page=1):
86 """Build URL for testapi request.
88 Build a URL for a testapi HTTP GET request using the provided parameters and
89 limiting the results to the tests whose criteria equals "PASS".
92 testapi_params: dictionary holding the parameters of the testapi
94 - mandatory keys: "project_name", "case_name"
95 - optional keys: "installer", "pod_name"
96 - ignored keys: "build_tag", "scenario", "version", "criteria".
98 page: (Optional) number of the results page to get.
102 url += f"?project={testapi_params['project_name']}"
103 url += f"&case={testapi_params['case_name']}"
105 if "installer" in testapi_params.keys():
106 url += f"&installer={testapi_params['installer']}"
107 if "pod_name" in testapi_params.keys():
108 url += f"&pod={testapi_params['pod_name']}"
110 url += '&criteria=PASS'
111 url += f"&page={page}"
115 def _do_testapi_request(self, testapi_url):
116 """Perform HTTP GET request on testapi.
118 Perform an HTTP GET request on testapi, check status code and return JSON
119 results as dictionary.
121 Args: testapi_url: a complete URL to request testapi results (with base
122 endpoint and parameters)
125 The JSON document from testapi as a Python dictionary
130 response = requests.get(testapi_url)
131 assert response.status_code == 200 # TODO: better error message
132 results = json.loads(response.text)
136 def equal_test_conditions(testapi_input, nfvbench_input):
137 """Check test conditions in behave scenario results record.
139 Check whether a behave scenario results record from testapi matches a given
140 nfvbench input, ie whether the record comes from a test done under the same
141 conditions (frame size, flow count, ...)
144 testapi_input: dict holding the test conditions of a behave scenario
145 results record from testapi
147 nfvbench_input: dict of nfvbench test parameters (reference)
149 The following dict keys are currently supported:
150 - mandatory keys: 'duration_sec', 'frame_sizes', 'flow_count', 'rate'
151 - optional keys: 'user_label', 'flavor_type'
153 Optional keys are taken into account only when they can be found in
154 `nfvbench_input`, else they are ignored.
157 True if test conditions match, else False.
160 # Select required keys (other keys can be not set or unconsistent between scenarios)
161 required_keys = ['duration_sec', 'frame_sizes', 'flow_count', 'rate']
162 if 'user_label' in nfvbench_input:
163 required_keys.append('user_label')
164 if 'flavor_type' in nfvbench_input:
165 required_keys.append('flavor_type')
168 testapi_subset = {k: testapi_input[k] for k in required_keys}
169 nfvbench_subset = {k: nfvbench_input[k] for k in required_keys}
170 return testapi_subset == nfvbench_subset
172 # Fail the comparison if a required key is missing from one of the dicts
176 def nfvbench_input_to_str(nfvbench_input: dict) -> str:
177 """Build string showing nfvbench input parameters used for results search
180 nfvbench_input: dict of nfvbench test parameters
183 for key in ['user_label', 'flavor_type', 'frame_sizes', 'flow_count', 'rate', 'duration_sec']:
184 if key in nfvbench_input:
185 string += f"{key}={nfvbench_input[key]} "