argus-alm 0.11.4__py3-none-any.whl → 0.11.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- argus/backend/plugins/driver_matrix_tests/model.py +5 -1
- argus/backend/plugins/sct/controller.py +30 -0
- argus/backend/plugins/sct/plugin.py +3 -1
- argus/backend/plugins/sct/service.py +158 -2
- argus/backend/plugins/sct/testrun.py +78 -13
- argus/backend/plugins/sct/types.py +38 -0
- argus/backend/plugins/sct/udt.py +13 -0
- argus/backend/service/build_system_monitor.py +21 -3
- argus/backend/service/event_service.py +18 -0
- argus/backend/service/stats.py +69 -15
- argus/backend/service/testrun.py +13 -21
- argus/backend/util/encoders.py +3 -0
- argus/client/driver_matrix_tests/client.py +19 -6
- argus/client/sct/client.py +31 -0
- {argus_alm-0.11.4.dist-info → argus_alm-0.11.6.dist-info}/METADATA +1 -1
- {argus_alm-0.11.4.dist-info → argus_alm-0.11.6.dist-info}/RECORD +18 -16
- {argus_alm-0.11.4.dist-info → argus_alm-0.11.6.dist-info}/WHEEL +1 -1
- {argus_alm-0.11.4.dist-info → argus_alm-0.11.6.dist-info}/LICENSE +0 -0
|
@@ -58,6 +58,10 @@ class DriverTestRun(PluginModelBase):
|
|
|
58
58
|
run.build_id = req.job_name
|
|
59
59
|
run.build_job_url = req.job_url
|
|
60
60
|
run.assign_categories()
|
|
61
|
+
try:
|
|
62
|
+
run.assignee = run.get_scheduled_assignee()
|
|
63
|
+
except Exception: # pylint: disable=broad-except
|
|
64
|
+
run.assignee = None
|
|
61
65
|
for key, value in req.test_environment.items():
|
|
62
66
|
env_info = EnvironmentInfo()
|
|
63
67
|
env_info.key = key
|
|
@@ -102,7 +106,7 @@ class DriverTestRun(PluginModelBase):
|
|
|
102
106
|
collection.suites.append(suite)
|
|
103
107
|
run.test_collection.append(collection)
|
|
104
108
|
|
|
105
|
-
run.status = run._determine_run_status()
|
|
109
|
+
run.status = run._determine_run_status().value
|
|
106
110
|
run.save()
|
|
107
111
|
return run
|
|
108
112
|
|
|
@@ -114,6 +114,36 @@ def sct_events_submit(run_id: str):
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
|
|
117
|
+
@bp.route("/<string:run_id>/gemini/submit", methods=["POST"])
|
|
118
|
+
@api_login_required
|
|
119
|
+
def sct_gemini_results_submit(run_id: str):
|
|
120
|
+
payload = get_payload(request)
|
|
121
|
+
result = SCTService.submit_gemini_results(run_id=run_id, gemini_data=payload["gemini_data"])
|
|
122
|
+
return {
|
|
123
|
+
"status": "ok",
|
|
124
|
+
"response": result
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
@bp.route("/<string:run_id>/performance/submit", methods=["POST"])
|
|
128
|
+
@api_login_required
|
|
129
|
+
def sct_performance_results_submit(run_id: str):
|
|
130
|
+
payload = get_payload(request)
|
|
131
|
+
result = SCTService.submit_performance_results(run_id=run_id, performance_results=payload["performance_results"])
|
|
132
|
+
return {
|
|
133
|
+
"status": "ok",
|
|
134
|
+
"response": result
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@bp.route("/<string:run_id>/performance/history", methods=["GET"])
|
|
138
|
+
@api_login_required
|
|
139
|
+
def sct_get_performance_history(run_id: str):
|
|
140
|
+
result = SCTService.get_performance_history_for_test(run_id=run_id)
|
|
141
|
+
return {
|
|
142
|
+
"status": "ok",
|
|
143
|
+
"response": result
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
117
147
|
@bp.route("/release/<string:release_name>/kernels", methods=["GET"])
|
|
118
148
|
@api_login_required
|
|
119
149
|
def sct_get_kernel_report(release_name: str):
|
|
@@ -12,6 +12,7 @@ from argus.backend.plugins.sct.udt import (
|
|
|
12
12
|
NemesisRunInfo,
|
|
13
13
|
NodeDescription,
|
|
14
14
|
PackageVersion,
|
|
15
|
+
PerformanceHDRHistogram,
|
|
15
16
|
)
|
|
16
17
|
|
|
17
18
|
|
|
@@ -31,5 +32,6 @@ class PluginInfo(PluginInfoBase):
|
|
|
31
32
|
CloudSetupDetails,
|
|
32
33
|
CloudNodesInfo,
|
|
33
34
|
CloudInstanceDetails,
|
|
34
|
-
PackageVersion
|
|
35
|
+
PackageVersion,
|
|
36
|
+
PerformanceHDRHistogram,
|
|
35
37
|
]
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
+
from functools import reduce
|
|
2
3
|
import logging
|
|
4
|
+
import math
|
|
3
5
|
from time import time
|
|
4
|
-
from
|
|
6
|
+
from flask import g
|
|
7
|
+
from argus.backend.models.web import ArgusEventTypes
|
|
8
|
+
from argus.backend.plugins.sct.testrun import SCTTestRun, SubtestType
|
|
9
|
+
from argus.backend.plugins.sct.types import GeminiResultsRequest, PerformanceResultsRequest
|
|
5
10
|
from argus.backend.plugins.sct.udt import (
|
|
6
11
|
CloudInstanceDetails,
|
|
7
12
|
CloudResource,
|
|
@@ -9,8 +14,11 @@ from argus.backend.plugins.sct.udt import (
|
|
|
9
14
|
NemesisRunInfo,
|
|
10
15
|
NodeDescription,
|
|
11
16
|
PackageVersion,
|
|
17
|
+
PerformanceHDRHistogram,
|
|
12
18
|
)
|
|
13
|
-
from argus.backend.
|
|
19
|
+
from argus.backend.service.event_service import EventService
|
|
20
|
+
from argus.backend.util.common import get_build_number
|
|
21
|
+
from argus.backend.util.enums import NemesisStatus, ResourceState, TestStatus
|
|
14
22
|
|
|
15
23
|
LOGGER = logging.getLogger(__name__)
|
|
16
24
|
|
|
@@ -91,6 +99,154 @@ class SCTService:
|
|
|
91
99
|
|
|
92
100
|
return "submitted"
|
|
93
101
|
|
|
102
|
+
@staticmethod
|
|
103
|
+
def submit_gemini_results(run_id: str, gemini_data: GeminiResultsRequest) -> str:
|
|
104
|
+
try:
|
|
105
|
+
run: SCTTestRun = SCTTestRun.get(id=run_id)
|
|
106
|
+
run.subtest_name = SubtestType.GEMINI.value
|
|
107
|
+
run.oracle_nodes_count = gemini_data.get("oracle_nodes_count")
|
|
108
|
+
run.oracle_node_ami_id = gemini_data.get("oracle_node_ami_id")
|
|
109
|
+
run.oracle_node_instance_type = gemini_data.get("oracle_node_instance_type")
|
|
110
|
+
run.oracle_node_scylla_version = gemini_data.get("oracle_node_scylla_version")
|
|
111
|
+
run.gemini_command = gemini_data.get("gemini_command")
|
|
112
|
+
run.gemini_version = gemini_data.get("gemini_version")
|
|
113
|
+
run.gemini_status = gemini_data.get("gemini_status")
|
|
114
|
+
run.gemini_seed = str(gemini_data.get("gemini_seed"))
|
|
115
|
+
run.gemini_write_ops = gemini_data.get("gemini_write_ops")
|
|
116
|
+
run.gemini_write_errors = gemini_data.get("gemini_write_errors")
|
|
117
|
+
run.gemini_read_ops = gemini_data.get("gemini_read_ops")
|
|
118
|
+
run.gemini_read_errors = gemini_data.get("gemini_read_errors")
|
|
119
|
+
run.save()
|
|
120
|
+
|
|
121
|
+
if run.gemini_status != "PASSED":
|
|
122
|
+
run.status = TestStatus.FAILED
|
|
123
|
+
EventService.create_run_event(kind=ArgusEventTypes.TestRunStatusChanged, body={
|
|
124
|
+
"message": "[{username}] Setting run status to {status} due to Gemini reporting following status: {gemini_status}",
|
|
125
|
+
"username": g.user.username,
|
|
126
|
+
"status": TestStatus.FAILED.value,
|
|
127
|
+
"gemini_status": run.gemini_status,
|
|
128
|
+
}, user_id=g.user.id, run_id=run_id, release_id=run.release_id, test_id=run.test_id)
|
|
129
|
+
run.save()
|
|
130
|
+
except SCTTestRun.DoesNotExist as exception:
|
|
131
|
+
LOGGER.error("Run %s not found for SCTTestRun", run_id)
|
|
132
|
+
raise SCTServiceException("Run not found", run_id) from exception
|
|
133
|
+
|
|
134
|
+
return "submitted"
|
|
135
|
+
|
|
136
|
+
@staticmethod
|
|
137
|
+
def submit_performance_results(run_id: str, performance_results: PerformanceResultsRequest):
|
|
138
|
+
# pylint: disable=too-many-statements
|
|
139
|
+
try:
|
|
140
|
+
run: SCTTestRun = SCTTestRun.get(id=run_id)
|
|
141
|
+
run.subtest_name = SubtestType.PERFORMANCE.value
|
|
142
|
+
run.perf_op_rate_average = performance_results.get("perf_op_rate_average")
|
|
143
|
+
run.perf_op_rate_total = performance_results.get("perf_op_rate_total")
|
|
144
|
+
run.perf_avg_latency_99th = performance_results.get("perf_avg_latency_99th")
|
|
145
|
+
run.perf_avg_latency_mean = performance_results.get("perf_avg_latency_mean")
|
|
146
|
+
run.perf_total_errors = performance_results.get("perf_total_errors")
|
|
147
|
+
run.stress_cmd = performance_results.get("stress_cmd")
|
|
148
|
+
run.test_name = performance_results.get("test_name")
|
|
149
|
+
run.save()
|
|
150
|
+
|
|
151
|
+
is_latency_test = "latency" in run.test_name
|
|
152
|
+
threshold_negative = -10
|
|
153
|
+
|
|
154
|
+
def cmp(lhs, rhs):
|
|
155
|
+
delta = rhs - lhs
|
|
156
|
+
change = int(math.fabs(delta) * 100 / rhs)
|
|
157
|
+
return change if delta >= 0 else change * -1
|
|
158
|
+
|
|
159
|
+
previous_runs = SCTTestRun.get_perf_results_for_test_name(run.build_id, run.start_time, run.test_name)
|
|
160
|
+
metrics_to_check = ["perf_avg_latency_99th", "perf_avg_latency_mean"] if is_latency_test else ["perf_op_rate_total"]
|
|
161
|
+
|
|
162
|
+
older_runs_by_version = {}
|
|
163
|
+
for prev_run in previous_runs:
|
|
164
|
+
if not older_runs_by_version.get(prev_run["scylla_version"]):
|
|
165
|
+
older_runs_by_version[prev_run["scylla_version"]] = []
|
|
166
|
+
older_runs_by_version[prev_run["scylla_version"]].append(prev_run)
|
|
167
|
+
|
|
168
|
+
regression_found = False
|
|
169
|
+
regression_info = {
|
|
170
|
+
"version": None,
|
|
171
|
+
"delta": None,
|
|
172
|
+
"id": None,
|
|
173
|
+
"metric": None,
|
|
174
|
+
"job_url": None,
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if performance_results["histograms"]:
|
|
178
|
+
for histogram in performance_results["histograms"]:
|
|
179
|
+
run.histograms = { k: PerformanceHDRHistogram(**v) for k, v in histogram.items() }
|
|
180
|
+
|
|
181
|
+
for version, runs in older_runs_by_version.items():
|
|
182
|
+
for metric in metrics_to_check:
|
|
183
|
+
# pylint: disable=cell-var-from-loop
|
|
184
|
+
best_run = sorted(runs, reverse=(not is_latency_test), key=lambda v: v[metric])[0]
|
|
185
|
+
last_run = runs[0]
|
|
186
|
+
|
|
187
|
+
metric_to_best = cmp(run[metric], best_run[metric])
|
|
188
|
+
metric_to_last = cmp(run[metric], last_run[metric])
|
|
189
|
+
if metric_to_last < threshold_negative:
|
|
190
|
+
regression_found = True
|
|
191
|
+
regression_info["metric"] = metric
|
|
192
|
+
regression_info["version"] = version
|
|
193
|
+
regression_info["job_url"] = last_run["build_job_url"]
|
|
194
|
+
regression_info["id"] = str(last_run["id"])
|
|
195
|
+
regression_info["delta"] = metric_to_last
|
|
196
|
+
break
|
|
197
|
+
|
|
198
|
+
if metric_to_best < threshold_negative:
|
|
199
|
+
regression_found = True
|
|
200
|
+
regression_info["metric"] = metric
|
|
201
|
+
regression_info["version"] = version
|
|
202
|
+
regression_info["job_url"] = best_run["build_job_url"]
|
|
203
|
+
regression_info["id"] = str(best_run["id"])
|
|
204
|
+
regression_info["delta"] = metric_to_best
|
|
205
|
+
break
|
|
206
|
+
|
|
207
|
+
if regression_found:
|
|
208
|
+
break
|
|
209
|
+
|
|
210
|
+
if regression_found:
|
|
211
|
+
run.status = TestStatus.FAILED.value
|
|
212
|
+
run.save()
|
|
213
|
+
EventService.create_run_event(kind=ArgusEventTypes.TestRunStatusChanged, body={
|
|
214
|
+
"message": "[{username}] Setting run status to {status} due to performance metric '{metric}' falling "
|
|
215
|
+
"below allowed threshold ({threshold_negative}): {delta}% compared to "
|
|
216
|
+
"<a href='/test/{test_id}/runs?additionalRuns[]={base_run_id}&additionalRuns[]={previous_run_id}'>This {version} (#{build_number}) run</a>",
|
|
217
|
+
"username": g.user.username,
|
|
218
|
+
"status": TestStatus.FAILED.value,
|
|
219
|
+
"metric": regression_info["metric"],
|
|
220
|
+
"threshold_negative": threshold_negative,
|
|
221
|
+
"delta": regression_info["delta"],
|
|
222
|
+
"test_id": str(run.test_id),
|
|
223
|
+
"base_run_id": str(run.id),
|
|
224
|
+
"previous_run_id": regression_info["id"],
|
|
225
|
+
"version": regression_info["version"],
|
|
226
|
+
"build_number": get_build_number(regression_info["job_url"])
|
|
227
|
+
}, user_id=g.user.id, run_id=run_id, release_id=run.release_id, test_id=run.test_id)
|
|
228
|
+
else:
|
|
229
|
+
# NOTE: This will override status set by SCT Events.
|
|
230
|
+
run.status = TestStatus.PASSED.value
|
|
231
|
+
run.save()
|
|
232
|
+
|
|
233
|
+
except SCTTestRun.DoesNotExist as exception:
|
|
234
|
+
LOGGER.error("Run %s not found for SCTTestRun", run_id)
|
|
235
|
+
raise SCTServiceException("Run not found", run_id) from exception
|
|
236
|
+
|
|
237
|
+
return "submitted"
|
|
238
|
+
|
|
239
|
+
@staticmethod
|
|
240
|
+
def get_performance_history_for_test(run_id: str):
|
|
241
|
+
try:
|
|
242
|
+
run: SCTTestRun = SCTTestRun.get(id=run_id)
|
|
243
|
+
rows = run.get_perf_results_for_test_name(build_id=run.build_id, start_time=run.start_time, test_name=run.test_name)
|
|
244
|
+
return rows
|
|
245
|
+
except SCTTestRun.DoesNotExist as exception:
|
|
246
|
+
LOGGER.error("Run %s not found for SCTTestRun", run_id)
|
|
247
|
+
raise SCTServiceException("Run not found", run_id) from exception
|
|
248
|
+
|
|
249
|
+
|
|
94
250
|
@staticmethod
|
|
95
251
|
def create_resource(run_id: str, resource_details: dict) -> str:
|
|
96
252
|
instance_details = CloudInstanceDetails(**resource_details.pop("instance_details"))
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
from enum import Enum
|
|
1
2
|
import logging
|
|
2
3
|
from datetime import datetime
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from typing import Optional
|
|
4
6
|
from uuid import UUID
|
|
5
7
|
|
|
6
8
|
from cassandra.cqlengine import columns
|
|
@@ -16,6 +18,7 @@ from argus.backend.plugins.sct.udt import (
|
|
|
16
18
|
EventsBySeverity,
|
|
17
19
|
NemesisRunInfo,
|
|
18
20
|
PackageVersion,
|
|
21
|
+
PerformanceHDRHistogram
|
|
19
22
|
)
|
|
20
23
|
|
|
21
24
|
LOGGER = logging.getLogger(__name__)
|
|
@@ -31,6 +34,11 @@ SCT_REGION_PROPERTY_MAP = {
|
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
|
|
37
|
+
class SubtestType(str, Enum):
|
|
38
|
+
GEMINI = "gemini"
|
|
39
|
+
PERFORMANCE = "performance"
|
|
40
|
+
|
|
41
|
+
|
|
34
42
|
@dataclass(init=True, repr=True, frozen=True)
|
|
35
43
|
class SCTTestRunSubmissionRequest():
|
|
36
44
|
schema_version: str
|
|
@@ -39,9 +47,9 @@ class SCTTestRunSubmissionRequest():
|
|
|
39
47
|
job_url: str
|
|
40
48
|
started_by: str
|
|
41
49
|
commit_id: str
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
sct_config: dict | None
|
|
51
|
+
runner_public_ip: Optional[str] = field(default=None)
|
|
52
|
+
runner_private_ip: Optional[str] = field(default=None)
|
|
45
53
|
|
|
46
54
|
|
|
47
55
|
class SCTTestRun(PluginModelBase):
|
|
@@ -49,6 +57,7 @@ class SCTTestRun(PluginModelBase):
|
|
|
49
57
|
_plugin_name = "scylla-cluster-tests"
|
|
50
58
|
|
|
51
59
|
# Test Details
|
|
60
|
+
test_name = columns.Text()
|
|
52
61
|
scm_revision_id = columns.Text()
|
|
53
62
|
started_by = columns.Text()
|
|
54
63
|
config_files = columns.List(value_type=columns.Text())
|
|
@@ -69,6 +78,33 @@ class SCTTestRun(PluginModelBase):
|
|
|
69
78
|
nemesis_data = columns.List(value_type=columns.UserDefinedType(user_type=NemesisRunInfo))
|
|
70
79
|
screenshots = columns.List(value_type=columns.Text())
|
|
71
80
|
|
|
81
|
+
# Subtest
|
|
82
|
+
subtest_name = columns.Text()
|
|
83
|
+
|
|
84
|
+
# Gemini-related fields
|
|
85
|
+
oracle_nodes_count = columns.Integer()
|
|
86
|
+
oracle_node_ami_id = columns.Text()
|
|
87
|
+
oracle_node_instance_type = columns.Text()
|
|
88
|
+
oracle_node_scylla_version = columns.Text()
|
|
89
|
+
gemini_command = columns.Text()
|
|
90
|
+
gemini_version = columns.Text()
|
|
91
|
+
gemini_status = columns.Text()
|
|
92
|
+
gemini_seed = columns.Text()
|
|
93
|
+
gemini_write_ops = columns.Integer()
|
|
94
|
+
gemini_write_errors = columns.Integer()
|
|
95
|
+
gemini_read_ops = columns.Integer()
|
|
96
|
+
gemini_read_errors = columns.Integer()
|
|
97
|
+
|
|
98
|
+
# Performance fields
|
|
99
|
+
perf_op_rate_average = columns.Double()
|
|
100
|
+
perf_op_rate_total = columns.Double()
|
|
101
|
+
perf_avg_latency_99th = columns.Double()
|
|
102
|
+
perf_avg_latency_mean = columns.Double()
|
|
103
|
+
perf_total_errors = columns.Double()
|
|
104
|
+
stress_cmd = columns.Text()
|
|
105
|
+
|
|
106
|
+
histograms = columns.List(value_type=columns.Map(key_type=columns.Text(), value_type=columns.UserDefinedType(user_type=PerformanceHDRHistogram)))
|
|
107
|
+
|
|
72
108
|
@classmethod
|
|
73
109
|
def _stats_query(cls) -> str:
|
|
74
110
|
return ("SELECT id, test_id, group_id, release_id, status, start_time, build_job_url, build_id, "
|
|
@@ -102,7 +138,17 @@ class SCTTestRun(PluginModelBase):
|
|
|
102
138
|
return list(rows)
|
|
103
139
|
|
|
104
140
|
@classmethod
|
|
105
|
-
def
|
|
141
|
+
def get_perf_results_for_test_name(cls, build_id: str, start_time: float, test_name: str):
|
|
142
|
+
cluster = ScyllaCluster.get()
|
|
143
|
+
query = cluster.prepare(f"SELECT build_id, packages, scylla_version, test_name, perf_op_rate_average, perf_op_rate_total, "
|
|
144
|
+
"perf_avg_latency_99th, perf_avg_latency_mean, perf_total_errors, id, start_time, build_job_url"
|
|
145
|
+
f" FROM {cls.table_name()} WHERE build_id = ? AND start_time < ? AND test_name = ? ALLOW FILTERING")
|
|
146
|
+
rows = cluster.session.execute(query=query, parameters=(build_id, start_time, test_name))
|
|
147
|
+
|
|
148
|
+
return list(rows)
|
|
149
|
+
|
|
150
|
+
@classmethod
|
|
151
|
+
def init_sct_run(cls, req: SCTTestRunSubmissionRequest):
|
|
106
152
|
run = cls()
|
|
107
153
|
run.build_id = req.job_name
|
|
108
154
|
run.assign_categories()
|
|
@@ -116,16 +162,35 @@ class SCTTestRun(PluginModelBase):
|
|
|
116
162
|
run.started_by = req.started_by
|
|
117
163
|
run.build_job_url = req.job_url
|
|
118
164
|
|
|
119
|
-
|
|
120
|
-
region_key = SCT_REGION_PROPERTY_MAP.get(backend, SCT_REGION_PROPERTY_MAP["default"])
|
|
121
|
-
raw_regions = req.sct_config.get(region_key) or "undefined_region"
|
|
122
|
-
regions = raw_regions.split() if isinstance(raw_regions, str) else raw_regions
|
|
165
|
+
return run
|
|
123
166
|
|
|
124
|
-
|
|
167
|
+
@classmethod
|
|
168
|
+
def from_sct_config(cls, req: SCTTestRunSubmissionRequest):
|
|
169
|
+
try:
|
|
170
|
+
run = cls.get(id=req.run_id)
|
|
171
|
+
except cls.DoesNotExist:
|
|
172
|
+
run = cls.init_sct_run(req)
|
|
173
|
+
run.save()
|
|
174
|
+
|
|
175
|
+
if req.sct_config:
|
|
176
|
+
backend = req.sct_config.get("cluster_backend")
|
|
177
|
+
region_key = SCT_REGION_PROPERTY_MAP.get(backend, SCT_REGION_PROPERTY_MAP["default"])
|
|
178
|
+
raw_regions = req.sct_config.get(region_key) or "undefined_region"
|
|
179
|
+
regions = raw_regions.split() if isinstance(raw_regions, str) else raw_regions
|
|
180
|
+
primary_region = regions[0]
|
|
181
|
+
if req.runner_public_ip: # NOTE: Legacy support, not needed otherwise
|
|
182
|
+
run.sct_runner_host = CloudInstanceDetails(
|
|
183
|
+
public_ip=req.runner_public_ip,
|
|
184
|
+
private_ip=req.runner_private_ip,
|
|
185
|
+
provider=backend,
|
|
186
|
+
region=primary_region,
|
|
187
|
+
)
|
|
188
|
+
run.cloud_setup = ResourceSetup.get_resource_setup(backend=backend, sct_config=req.sct_config)
|
|
189
|
+
|
|
190
|
+
run.config_files = req.sct_config.get("config_files")
|
|
191
|
+
run.region_name = regions
|
|
192
|
+
run.save()
|
|
125
193
|
|
|
126
|
-
run.config_files = req.sct_config.get("config_files")
|
|
127
|
-
run.region_name = regions
|
|
128
|
-
run.save()
|
|
129
194
|
return run
|
|
130
195
|
|
|
131
196
|
def get_resources(self) -> list[CloudResource]:
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from typing import TypedDict
|
|
2
|
+
|
|
3
|
+
class RawHDRHistogram(TypedDict):
|
|
4
|
+
start_time: int
|
|
5
|
+
percentile_90: float
|
|
6
|
+
percentile_50: float
|
|
7
|
+
percentile_99_999: float
|
|
8
|
+
percentile_95: float
|
|
9
|
+
end_time: float
|
|
10
|
+
percentile_99_99: float
|
|
11
|
+
percentile_99: float
|
|
12
|
+
stddev: float
|
|
13
|
+
percentile_99_9: float
|
|
14
|
+
|
|
15
|
+
class GeminiResultsRequest(TypedDict):
|
|
16
|
+
oracle_nodes_count: int
|
|
17
|
+
oracle_node_ami_id: str
|
|
18
|
+
oracle_node_instance_type: str
|
|
19
|
+
oracle_node_scylla_version: str
|
|
20
|
+
gemini_command: str
|
|
21
|
+
gemini_version: str
|
|
22
|
+
gemini_status: str
|
|
23
|
+
gemini_seed: str
|
|
24
|
+
gemini_write_ops: int
|
|
25
|
+
gemini_write_errors: int
|
|
26
|
+
gemini_read_ops: int
|
|
27
|
+
gemini_read_errors: int
|
|
28
|
+
|
|
29
|
+
class PerformanceResultsRequest(TypedDict):
|
|
30
|
+
test_name: str
|
|
31
|
+
stress_cmd: str
|
|
32
|
+
perf_op_rate_average: float
|
|
33
|
+
perf_op_rate_total: float
|
|
34
|
+
perf_avg_latency_99th: float
|
|
35
|
+
perf_avg_latency_mean: float
|
|
36
|
+
perf_total_errors: str
|
|
37
|
+
|
|
38
|
+
histograms: list[dict[str, RawHDRHistogram]] | None
|
argus/backend/plugins/sct/udt.py
CHANGED
|
@@ -78,3 +78,16 @@ class NemesisRunInfo(UserType):
|
|
|
78
78
|
start_time = columns.Integer()
|
|
79
79
|
end_time = columns.Integer()
|
|
80
80
|
stack_trace = columns.Text()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class PerformanceHDRHistogram(UserType):
|
|
84
|
+
start_time = columns.Integer()
|
|
85
|
+
percentile_90 = columns.Float()
|
|
86
|
+
percentile_50 = columns.Float()
|
|
87
|
+
percentile_99_999 = columns.Float()
|
|
88
|
+
percentile_95 = columns.Float()
|
|
89
|
+
end_time = columns.Float()
|
|
90
|
+
percentile_99_99 = columns.Float()
|
|
91
|
+
percentile_99 = columns.Float()
|
|
92
|
+
stddev = columns.Float()
|
|
93
|
+
percentile_99_9 = columns.Float()
|
|
@@ -78,7 +78,7 @@ class JenkinsMonitor(ArgusTestsMonitor):
|
|
|
78
78
|
def collect(self):
|
|
79
79
|
click.echo("Collecting new tests from jenkins")
|
|
80
80
|
all_jobs = self._jenkins.get_all_jobs()
|
|
81
|
-
all_monitored_folders = [job for job in all_jobs if job["
|
|
81
|
+
all_monitored_folders = [job for job in all_jobs if job["fullname"] in self._monitored_releases]
|
|
82
82
|
for release in all_monitored_folders:
|
|
83
83
|
LOGGER.info("Processing release %s", release["name"])
|
|
84
84
|
try:
|
|
@@ -89,8 +89,23 @@ class JenkinsMonitor(ArgusTestsMonitor):
|
|
|
89
89
|
saved_release = self.create_release(release["name"])
|
|
90
90
|
self._existing_releases.append(saved_release)
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
try:
|
|
93
|
+
groups = self.collect_groups_for_release(release["jobs"])
|
|
94
|
+
except KeyError:
|
|
95
|
+
LOGGER.error("Empty release!\n %s", release)
|
|
96
|
+
continue
|
|
93
97
|
folder_stack = [dict(parent_name="", parent_display_name="", group=g) for g in reversed(groups)]
|
|
98
|
+
root_folder = {
|
|
99
|
+
"parent_name": "",
|
|
100
|
+
"parent_display_name": "",
|
|
101
|
+
"group": {
|
|
102
|
+
"name": f"{release['fullname']}-root",
|
|
103
|
+
"displayName": "-- root directory --",
|
|
104
|
+
"fullname": release["fullname"],
|
|
105
|
+
"jobs": self.collect_root_folder_jobs(release["jobs"]),
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
folder_stack.append(root_folder)
|
|
94
109
|
while len(folder_stack) != 0:
|
|
95
110
|
group_dict = folder_stack.pop()
|
|
96
111
|
group = group_dict["group"]
|
|
@@ -104,7 +119,7 @@ class JenkinsMonitor(ArgusTestsMonitor):
|
|
|
104
119
|
LOGGER.warning(
|
|
105
120
|
"Group %s for release %s doesn't exist, creating...", group_name, saved_release.name)
|
|
106
121
|
try:
|
|
107
|
-
display_name = self._jenkins.get_job_info(name=group["fullname"])["displayName"]
|
|
122
|
+
display_name = group.get("displayName", self._jenkins.get_job_info(name=group["fullname"])["displayName"])
|
|
108
123
|
display_name = display_name if not group_dict[
|
|
109
124
|
"parent_display_name"] else f"{group_dict['parent_display_name']} - {display_name}"
|
|
110
125
|
except Exception:
|
|
@@ -138,3 +153,6 @@ class JenkinsMonitor(ArgusTestsMonitor):
|
|
|
138
153
|
groups = [group for group in groups if self.check_filter(group["name"])]
|
|
139
154
|
|
|
140
155
|
return groups
|
|
156
|
+
|
|
157
|
+
def collect_root_folder_jobs(self, jobs):
|
|
158
|
+
return [job for job in jobs if "WorkflowJob" in job["_class"]]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
import json
|
|
3
|
+
from argus.backend.models.web import ArgusEvent, ArgusEventTypes
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class EventService:
|
|
7
|
+
@staticmethod
|
|
8
|
+
def create_run_event(kind: ArgusEventTypes, body: dict, user_id=None, run_id=None, release_id=None, group_id=None, test_id=None):
|
|
9
|
+
event = ArgusEvent()
|
|
10
|
+
event.release_id = release_id
|
|
11
|
+
event.group_id = group_id
|
|
12
|
+
event.test_id = test_id
|
|
13
|
+
event.user_id = user_id
|
|
14
|
+
event.run_id = run_id
|
|
15
|
+
event.body = json.dumps(body, ensure_ascii=True, separators=(',', ':'))
|
|
16
|
+
event.kind = kind.value
|
|
17
|
+
event.created_at = datetime.utcnow()
|
|
18
|
+
event.save()
|
argus/backend/service/stats.py
CHANGED
|
@@ -76,6 +76,70 @@ class ComparableTestStatus:
|
|
|
76
76
|
return self._get_prio() <= __o._get_prio()
|
|
77
77
|
|
|
78
78
|
|
|
79
|
+
class ComparableTestInvestigationStatus:
|
|
80
|
+
PRIORITY_MAP = {
|
|
81
|
+
TestInvestigationStatus.NOT_INVESTIGATED: 10,
|
|
82
|
+
TestInvestigationStatus.IN_PROGRESS: 9,
|
|
83
|
+
TestInvestigationStatus.INVESTIGATED: 8,
|
|
84
|
+
TestInvestigationStatus.IGNORED: 7,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
def __init__(self, status: TestInvestigationStatus):
|
|
88
|
+
self._status = status
|
|
89
|
+
|
|
90
|
+
def _get_prio(self):
|
|
91
|
+
return self.PRIORITY_MAP.get(self._status, 0)
|
|
92
|
+
|
|
93
|
+
def __eq__(self, __o: object) -> bool:
|
|
94
|
+
if not isinstance(__o, ComparableTestInvestigationStatus):
|
|
95
|
+
return False
|
|
96
|
+
return self._get_prio() == __o._get_prio()
|
|
97
|
+
|
|
98
|
+
def __ne__(self, __o: object) -> bool:
|
|
99
|
+
if not isinstance(__o, ComparableTestInvestigationStatus):
|
|
100
|
+
return False
|
|
101
|
+
return not self.__eq__(__o)
|
|
102
|
+
|
|
103
|
+
def __lt__(self, __o: object) -> bool:
|
|
104
|
+
if not isinstance(__o, ComparableTestInvestigationStatus):
|
|
105
|
+
return False
|
|
106
|
+
return self._get_prio() < __o._get_prio()
|
|
107
|
+
|
|
108
|
+
def __gt__(self, __o: object) -> bool:
|
|
109
|
+
if not isinstance(__o, ComparableTestInvestigationStatus):
|
|
110
|
+
return False
|
|
111
|
+
return self._get_prio() > __o._get_prio()
|
|
112
|
+
|
|
113
|
+
def __ge__(self, __o: object) -> bool:
|
|
114
|
+
if not isinstance(__o, ComparableTestInvestigationStatus):
|
|
115
|
+
return False
|
|
116
|
+
return self._get_prio() >= __o._get_prio()
|
|
117
|
+
|
|
118
|
+
def __le__(self, __o: object) -> bool:
|
|
119
|
+
if not isinstance(__o, ComparableTestInvestigationStatus):
|
|
120
|
+
return False
|
|
121
|
+
return self._get_prio() <= __o._get_prio()
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def generate_field_status_map(
|
|
125
|
+
last_runs: list[TestRunStatRow],
|
|
126
|
+
field_name = "status",
|
|
127
|
+
container_class = TestStatus,
|
|
128
|
+
cmp_class = ComparableTestStatus
|
|
129
|
+
) -> dict[int, str]:
|
|
130
|
+
|
|
131
|
+
status_map = {}
|
|
132
|
+
for run in last_runs:
|
|
133
|
+
run_number = get_build_number(run["build_job_url"])
|
|
134
|
+
match status := status_map.get(run_number):
|
|
135
|
+
case str():
|
|
136
|
+
if cmp_class(container_class(status)) < cmp_class(container_class(run[field_name])):
|
|
137
|
+
status_map[run_number] = run[field_name]
|
|
138
|
+
case _:
|
|
139
|
+
status_map[run_number] = run[field_name]
|
|
140
|
+
return status_map
|
|
141
|
+
|
|
142
|
+
|
|
79
143
|
class ReleaseStats:
|
|
80
144
|
def __init__(self, release: ArgusRelease) -> None:
|
|
81
145
|
self.release = release
|
|
@@ -227,18 +291,6 @@ class TestStats:
|
|
|
227
291
|
"hasComments": self.has_comments
|
|
228
292
|
}
|
|
229
293
|
|
|
230
|
-
def _generate_status_map(self, last_runs: list[TestRunStatRow]) -> dict[int, str]:
|
|
231
|
-
status_map = {}
|
|
232
|
-
for run in last_runs:
|
|
233
|
-
run_number = get_build_number(run["build_job_url"])
|
|
234
|
-
match status := status_map.get(run_number):
|
|
235
|
-
case str():
|
|
236
|
-
if ComparableTestStatus(TestStatus(status)) < ComparableTestStatus(TestStatus(run["status"])):
|
|
237
|
-
status_map[run_number] = run["status"]
|
|
238
|
-
case _:
|
|
239
|
-
status_map[run_number] = run["status"]
|
|
240
|
-
return status_map
|
|
241
|
-
|
|
242
294
|
def collect(self, limited=False):
|
|
243
295
|
|
|
244
296
|
# TODO: Parametrize run limit
|
|
@@ -252,10 +304,12 @@ class TestStats:
|
|
|
252
304
|
self.status = TestStatus.NOT_RUN if self.is_scheduled else TestStatus.NOT_PLANNED
|
|
253
305
|
self.parent_group.increment_status(status=self.status)
|
|
254
306
|
return
|
|
255
|
-
status_map =
|
|
307
|
+
status_map = generate_field_status_map(last_runs)
|
|
308
|
+
investigation_status_map = generate_field_status_map(
|
|
309
|
+
last_runs, "investigation_status", TestInvestigationStatus, ComparableTestInvestigationStatus)
|
|
256
310
|
|
|
257
311
|
self.status = status_map.get(get_build_number(last_run["build_job_url"]))
|
|
258
|
-
self.investigation_status =
|
|
312
|
+
self.investigation_status = investigation_status_map.get(get_build_number(last_run["build_job_url"]))
|
|
259
313
|
self.start_time = last_run["start_time"]
|
|
260
314
|
|
|
261
315
|
self.parent_group.increment_status(status=self.status)
|
|
@@ -300,7 +354,7 @@ class ReleaseStatsCollector:
|
|
|
300
354
|
|
|
301
355
|
if self.release_version:
|
|
302
356
|
self.release_rows = list(
|
|
303
|
-
filter(lambda row: row["scylla_version"] == self.release_version, self.release_rows))
|
|
357
|
+
filter(lambda row: row["scylla_version"] == self.release_version or not row["scylla_version"], self.release_rows))
|
|
304
358
|
|
|
305
359
|
self.release_stats = ReleaseStats(release=self.release)
|
|
306
360
|
self.release_stats.collect(rows=self.release_rows, limited=limited, force=force)
|
argus/backend/service/testrun.py
CHANGED
|
@@ -30,7 +30,9 @@ from argus.backend.plugins.core import PluginInfoBase, PluginModelBase
|
|
|
30
30
|
|
|
31
31
|
from argus.backend.plugins.loader import AVAILABLE_PLUGINS
|
|
32
32
|
from argus.backend.events.event_processors import EVENT_PROCESSORS
|
|
33
|
+
from argus.backend.service.event_service import EventService
|
|
33
34
|
from argus.backend.service.notification_manager import NotificationManagerService
|
|
35
|
+
from argus.backend.service.stats import ComparableTestStatus
|
|
34
36
|
from argus.backend.util.common import get_build_number, strip_html_tags
|
|
35
37
|
from argus.backend.util.enums import TestInvestigationStatus, TestStatus
|
|
36
38
|
|
|
@@ -80,6 +82,8 @@ class TestRunService:
|
|
|
80
82
|
for row in last_runs:
|
|
81
83
|
row["build_number"] = get_build_number(build_job_url=row["build_job_url"])
|
|
82
84
|
|
|
85
|
+
last_runs = sorted(last_runs, reverse=True, key=lambda run: (run["build_number"], ComparableTestStatus(TestStatus(run["status"]))))
|
|
86
|
+
|
|
83
87
|
return last_runs
|
|
84
88
|
|
|
85
89
|
def get_runs_by_id(self, test_id: UUID, runs: list[UUID]): # FIXME: Not needed, use get_run and individual polling
|
|
@@ -108,7 +112,7 @@ class TestRunService:
|
|
|
108
112
|
run.status = new_status.value
|
|
109
113
|
run.save()
|
|
110
114
|
|
|
111
|
-
|
|
115
|
+
EventService.create_run_event(
|
|
112
116
|
kind=ArgusEventTypes.TestRunStatusChanged,
|
|
113
117
|
body={
|
|
114
118
|
"message": "Status was changed from {old_status} to {new_status} by {username}",
|
|
@@ -136,7 +140,7 @@ class TestRunService:
|
|
|
136
140
|
run.investigation_status = new_status.value
|
|
137
141
|
run.save()
|
|
138
142
|
|
|
139
|
-
|
|
143
|
+
EventService.create_run_event(
|
|
140
144
|
kind=ArgusEventTypes.TestRunStatusChanged,
|
|
141
145
|
body={
|
|
142
146
|
"message": "Investigation status was changed from {old_status} to {new_status} by {username}",
|
|
@@ -175,7 +179,7 @@ class TestRunService:
|
|
|
175
179
|
LOGGER.warning("Non existent assignee was present on the run %s for test %s: %s",
|
|
176
180
|
run_id, test_id, old_assignee)
|
|
177
181
|
old_assignee = None
|
|
178
|
-
|
|
182
|
+
EventService.create_run_event(
|
|
179
183
|
kind=ArgusEventTypes.AssigneeChanged,
|
|
180
184
|
body={
|
|
181
185
|
"message": "Assignee was changed from \"{old_user}\" to \"{new_user}\" by {username}",
|
|
@@ -244,7 +248,7 @@ class TestRunService:
|
|
|
244
248
|
content_params=params
|
|
245
249
|
)
|
|
246
250
|
|
|
247
|
-
|
|
251
|
+
EventService.create_run_event(kind=ArgusEventTypes.TestRunCommentPosted, body={
|
|
248
252
|
"message": "A comment was posted by {username}",
|
|
249
253
|
"username": g.user.username
|
|
250
254
|
}, user_id=g.user.id, run_id=run_id, release_id=release.id, test_id=test.id)
|
|
@@ -257,7 +261,7 @@ class TestRunService:
|
|
|
257
261
|
raise Exception("Unable to delete other user comments")
|
|
258
262
|
comment.delete()
|
|
259
263
|
|
|
260
|
-
|
|
264
|
+
EventService.create_run_event(kind=ArgusEventTypes.TestRunCommentDeleted, body={
|
|
261
265
|
"message": "A comment was deleted by {username}",
|
|
262
266
|
"username": g.user.username
|
|
263
267
|
}, user_id=g.user.id, run_id=run_id, release_id=comment.release_id, test_id=test_id)
|
|
@@ -273,25 +277,13 @@ class TestRunService:
|
|
|
273
277
|
comment.mentions = mentions
|
|
274
278
|
comment.save()
|
|
275
279
|
|
|
276
|
-
|
|
280
|
+
EventService.create_run_event(kind=ArgusEventTypes.TestRunCommentUpdated, body={
|
|
277
281
|
"message": "A comment was edited by {username}",
|
|
278
282
|
"username": g.user.username
|
|
279
283
|
}, user_id=g.user.id, run_id=run_id, release_id=comment.release_id, test_id=test_id)
|
|
280
284
|
|
|
281
285
|
return self.get_run_comments(run_id=run_id)
|
|
282
286
|
|
|
283
|
-
def create_run_event(self, kind: ArgusEventTypes, body: dict, user_id=None, run_id=None, release_id=None, group_id=None, test_id=None):
|
|
284
|
-
event = ArgusEvent()
|
|
285
|
-
event.release_id = release_id
|
|
286
|
-
event.group_id = group_id
|
|
287
|
-
event.test_id = test_id
|
|
288
|
-
event.user_id = user_id
|
|
289
|
-
event.run_id = run_id
|
|
290
|
-
event.body = json.dumps(body, ensure_ascii=True, separators=(',', ':'))
|
|
291
|
-
event.kind = kind.value
|
|
292
|
-
event.created_at = datetime.utcnow()
|
|
293
|
-
event.save()
|
|
294
|
-
|
|
295
287
|
def get_run_events(self, run_id: UUID):
|
|
296
288
|
response = {}
|
|
297
289
|
all_events = ArgusEvent.filter(run_id=run_id).all()
|
|
@@ -358,7 +350,7 @@ class TestRunService:
|
|
|
358
350
|
new_issue.last_status = issue_state.get("state")
|
|
359
351
|
new_issue.save()
|
|
360
352
|
|
|
361
|
-
|
|
353
|
+
EventService.create_run_event(
|
|
362
354
|
kind=ArgusEventTypes.TestRunIssueAdded,
|
|
363
355
|
body={
|
|
364
356
|
"message": "An issue titled \"{title}\" was added by {username}",
|
|
@@ -409,7 +401,7 @@ class TestRunService:
|
|
|
409
401
|
def delete_github_issue(self, issue_id: UUID) -> dict:
|
|
410
402
|
issue: ArgusGithubIssue = ArgusGithubIssue.get(id=issue_id)
|
|
411
403
|
|
|
412
|
-
|
|
404
|
+
EventService.create_run_event(
|
|
413
405
|
kind=ArgusEventTypes.TestRunIssueRemoved,
|
|
414
406
|
body={
|
|
415
407
|
"message": "An issue titled \"{title}\" was removed by {username}",
|
|
@@ -448,7 +440,7 @@ class TestRunService:
|
|
|
448
440
|
run.status = TestStatus.ABORTED.value
|
|
449
441
|
run.save()
|
|
450
442
|
|
|
451
|
-
|
|
443
|
+
EventService.create_run_event(
|
|
452
444
|
kind=ArgusEventTypes.TestRunStatusChanged,
|
|
453
445
|
body={
|
|
454
446
|
"message": "Run was automatically terminated due to not responding for more than 45 minutes "
|
argus/backend/util/encoders.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
+
import logging
|
|
2
3
|
from json.encoder import JSONEncoder
|
|
3
4
|
from uuid import UUID
|
|
4
5
|
|
|
@@ -6,6 +7,8 @@ import cassandra.cqlengine.usertype as ut
|
|
|
6
7
|
import cassandra.cqlengine.models as m
|
|
7
8
|
|
|
8
9
|
|
|
10
|
+
LOGGER = logging.getLogger(__name__)
|
|
11
|
+
|
|
9
12
|
class ArgusJSONEncoder(JSONEncoder):
|
|
10
13
|
def default(self, o):
|
|
11
14
|
match o:
|
|
@@ -16,6 +16,7 @@ from argus.backend.plugins.driver_matrix_tests.raw_types import RawMatrixTestRes
|
|
|
16
16
|
|
|
17
17
|
LOGGER = logging.getLogger(__name__)
|
|
18
18
|
|
|
19
|
+
TestTypeType = Literal['java', 'cpp', 'python', 'gocql']
|
|
19
20
|
|
|
20
21
|
class AdaptedXUnitData(TypedDict):
|
|
21
22
|
timestamp: str
|
|
@@ -52,6 +53,14 @@ def cpp_driver_matrix_adapter(xml: ElementTree.ElementTree) -> AdaptedXUnitData:
|
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
|
|
56
|
+
def gocql_driver_matrix_adapter(xml: ElementTree.ElementTree) -> AdaptedXUnitData:
|
|
57
|
+
testsuites = list(xml.getroot().iter("testsuite"))
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
"timestamp": testsuites[0].attrib.get("timestamp"),
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
55
64
|
def generic_adapter(xml: ElementTree.ElementTree) -> AdaptedXUnitData:
|
|
56
65
|
return {
|
|
57
66
|
"timestamp": datetime.utcnow().isoformat()
|
|
@@ -66,6 +75,7 @@ class ArgusDriverMatrixClient(ArgusClient):
|
|
|
66
75
|
"java": java_driver_matrix_adapter,
|
|
67
76
|
"cpp": cpp_driver_matrix_adapter,
|
|
68
77
|
"python": python_driver_matrix_adapter,
|
|
78
|
+
"gocql": gocql_driver_matrix_adapter,
|
|
69
79
|
}
|
|
70
80
|
|
|
71
81
|
def __init__(self, run_id: UUID, auth_token: str, base_url: str, api_version="v1") -> None:
|
|
@@ -107,8 +117,11 @@ class ArgusDriverMatrixClient(ArgusClient):
|
|
|
107
117
|
|
|
108
118
|
return raw_cases
|
|
109
119
|
|
|
110
|
-
def get_driver_info(self, xml_name: str) -> dict[str, str]:
|
|
111
|
-
|
|
120
|
+
def get_driver_info(self, xml_name: str, test_type: TestTypeType) -> dict[str, str]:
|
|
121
|
+
if test_type == "cpp":
|
|
122
|
+
filename_re = r"TEST-(?P<driver_name>[\w]*)-(?P<version>[\d\.]*)\.xml"
|
|
123
|
+
else:
|
|
124
|
+
filename_re = r"(?P<name>[\w]*)\.(?P<driver_name>[\w]*)\.(?P<proto>v\d)\.(?P<version>[\d\.]*)\.xml"
|
|
112
125
|
|
|
113
126
|
match = re.match(filename_re, xml_name)
|
|
114
127
|
|
|
@@ -124,14 +137,14 @@ class ArgusDriverMatrixClient(ArgusClient):
|
|
|
124
137
|
|
|
125
138
|
return total - errors - skipped - failures
|
|
126
139
|
|
|
127
|
-
def parse_result_xml(self, xml_path: Path, test_type:
|
|
140
|
+
def parse_result_xml(self, xml_path: Path, test_type: TestTypeType) -> RawMatrixTestResult:
|
|
128
141
|
with xml_path.open(mode="rt", encoding="utf-8") as xml_file:
|
|
129
142
|
xml = ElementTree.parse(source=xml_file)
|
|
130
143
|
LOGGER.info("%s", pformat(xml))
|
|
131
144
|
testsuites = xml.getroot()
|
|
132
145
|
adapted_data = self.TEST_ADAPTERS.get(test_type, generic_adapter)(xml)
|
|
133
146
|
|
|
134
|
-
driver_info = self.get_driver_info(xml_path.name)
|
|
147
|
+
driver_info = self.get_driver_info(xml_path.name, test_type)
|
|
135
148
|
test_collection = {
|
|
136
149
|
"timestamp": adapted_data["timestamp"],
|
|
137
150
|
"name": xml_path.stem,
|
|
@@ -171,7 +184,7 @@ class ArgusDriverMatrixClient(ArgusClient):
|
|
|
171
184
|
"suites": all_suites
|
|
172
185
|
}
|
|
173
186
|
|
|
174
|
-
def get_results(self, result_path: str, test_type:
|
|
187
|
+
def get_results(self, result_path: str, test_type: TestTypeType) -> list[RawMatrixTestResult]:
|
|
175
188
|
xmls = glob(f"{result_path}/**/*.xml", recursive=True)
|
|
176
189
|
LOGGER.info("Will use following XMLs: %s", pformat(xmls))
|
|
177
190
|
results = []
|
|
@@ -180,7 +193,7 @@ class ArgusDriverMatrixClient(ArgusClient):
|
|
|
180
193
|
results.append(result)
|
|
181
194
|
return results
|
|
182
195
|
|
|
183
|
-
def submit(self, build_id: str, build_url: str, env_path: str, result_path: str, test_type:
|
|
196
|
+
def submit(self, build_id: str, build_url: str, env_path: str, result_path: str, test_type: TestTypeType):
|
|
184
197
|
env = self.parse_build_environment(env_path)
|
|
185
198
|
results = self.get_results(result_path, test_type)
|
|
186
199
|
|
argus/client/sct/client.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from uuid import UUID
|
|
2
2
|
from dataclasses import asdict
|
|
3
|
+
from argus.backend.plugins.sct.types import GeminiResultsRequest, PerformanceResultsRequest
|
|
3
4
|
from argus.backend.util.enums import ResourceState, TestStatus
|
|
4
5
|
from argus.client.base import ArgusClient
|
|
5
6
|
from argus.client.sct.types import EventsInfo, LogLink, Package
|
|
@@ -17,6 +18,8 @@ class ArgusSCTClient(ArgusClient):
|
|
|
17
18
|
SET_SCT_RUNNER = "/sct/$id/sct_runner/set"
|
|
18
19
|
UPDATE_SHARDS_FOR_RESOURCE = "/sct/$id/resource/$name/shards"
|
|
19
20
|
SUBMIT_NEMESIS = "/sct/$id/nemesis/submit"
|
|
21
|
+
SUBMIT_GEMINI_RESULTS = "/sct/$id/gemini/submit"
|
|
22
|
+
SUBMIT_PERFORMANCE_RESULTS = "/sct/$id/performance/submit"
|
|
20
23
|
FINALIZE_NEMESIS = "/sct/$id/nemesis/finalize"
|
|
21
24
|
SUBMIT_EVENTS = "/sct/$id/events/submit"
|
|
22
25
|
|
|
@@ -113,6 +116,34 @@ class ArgusSCTClient(ArgusClient):
|
|
|
113
116
|
)
|
|
114
117
|
self.check_response(response)
|
|
115
118
|
|
|
119
|
+
def submit_gemini_results(self, gemini_data: GeminiResultsRequest) -> None:
|
|
120
|
+
"""
|
|
121
|
+
Submits gemini results such as oracle node information and gemini command & its results
|
|
122
|
+
"""
|
|
123
|
+
response = self.post(
|
|
124
|
+
endpoint=self.Routes.SUBMIT_GEMINI_RESULTS,
|
|
125
|
+
location_params={"id": str(self.run_id)},
|
|
126
|
+
body={
|
|
127
|
+
**self.generic_body,
|
|
128
|
+
"gemini_data": gemini_data,
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
self.check_response(response)
|
|
132
|
+
|
|
133
|
+
def submit_performance_results(self, performance_results: PerformanceResultsRequest) -> None:
|
|
134
|
+
"""
|
|
135
|
+
Submits results of a performance run. Things such as throughput stats, overall and per op
|
|
136
|
+
"""
|
|
137
|
+
response = self.post(
|
|
138
|
+
endpoint=self.Routes.SUBMIT_PERFORMANCE_RESULTS,
|
|
139
|
+
location_params={"id": str(self.run_id)},
|
|
140
|
+
body={
|
|
141
|
+
**self.generic_body,
|
|
142
|
+
"performance_results": performance_results,
|
|
143
|
+
}
|
|
144
|
+
)
|
|
145
|
+
self.check_response(response)
|
|
146
|
+
|
|
116
147
|
def create_resource(self, name: str, resource_type: str, public_ip: str, private_ip: str,
|
|
117
148
|
region: str, provider: str, shards_amount: int, state=ResourceState.RUNNING) -> None:
|
|
118
149
|
"""
|
|
@@ -22,42 +22,44 @@ argus/backend/models/web.py,sha256=E85BcV3ESTTweYmNrFSG9hDYDIuaTLlnbZul2J8sPQg,1
|
|
|
22
22
|
argus/backend/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
23
|
argus/backend/plugins/core.py,sha256=CYwYz5BMQRZdqGXFKoiIi_AEKMunBSyE8zCXmYarjGI,7427
|
|
24
24
|
argus/backend/plugins/driver_matrix_tests/controller.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
|
-
argus/backend/plugins/driver_matrix_tests/model.py,sha256=
|
|
25
|
+
argus/backend/plugins/driver_matrix_tests/model.py,sha256=VAX2JADA5l97IQu1p3ojqsxNoX6e--CgZtvd8Ynp3yE,5755
|
|
26
26
|
argus/backend/plugins/driver_matrix_tests/plugin.py,sha256=NZN6lpHFpNuBkv2D_gGHh03402z0qlOeCWjgZXRs2c4,637
|
|
27
27
|
argus/backend/plugins/driver_matrix_tests/raw_types.py,sha256=A108HCnv5q0RHfNRhUJrTpRy3fG7sPxr9Sk4gfsbooU,600
|
|
28
28
|
argus/backend/plugins/driver_matrix_tests/udt.py,sha256=6lydzF3AQHm3GR5bKEDu1xLPYsLaBL3o-wu9NpabbqA,1134
|
|
29
29
|
argus/backend/plugins/loader.py,sha256=0MmGNBVle51tIyH7lA7Lz2L8PcGKGub9vyFuwnLm840,1320
|
|
30
|
-
argus/backend/plugins/sct/controller.py,sha256=
|
|
31
|
-
argus/backend/plugins/sct/plugin.py,sha256
|
|
30
|
+
argus/backend/plugins/sct/controller.py,sha256=ek4gNiraDzd81QMpHKCzpugUSkwbXCHKAPsjD9ol6CE,4791
|
|
31
|
+
argus/backend/plugins/sct/plugin.py,sha256=3TlX5NAd7bv4aMI9FJNJLkf8o2bcahZhZobAV4xAbzU,965
|
|
32
32
|
argus/backend/plugins/sct/resource_setup.py,sha256=hwfAOu-oKOH42tjtzJhiqwq_MtUE9_HevoFyql8JKqY,10120
|
|
33
|
-
argus/backend/plugins/sct/service.py,sha256=
|
|
34
|
-
argus/backend/plugins/sct/testrun.py,sha256
|
|
35
|
-
argus/backend/plugins/sct/
|
|
33
|
+
argus/backend/plugins/sct/service.py,sha256=VR9fEwc43Y9nFW9__Lvbm613VIb1lp5Y3P3dXm-LwRk,17618
|
|
34
|
+
argus/backend/plugins/sct/testrun.py,sha256=-ulaIZ1Dn-umx3LAJOzZkF3C_3JNQLncbsLCCf7Y7ks,8931
|
|
35
|
+
argus/backend/plugins/sct/types.py,sha256=avLL3fzug_n6GbtlW8bcPewcDGi1DxHFD0vp-JZ7hdQ,969
|
|
36
|
+
argus/backend/plugins/sct/udt.py,sha256=KVVkzaiEeyKs2ntFjXQ6BpoBjX4kLuSK-csgIVyNK00,2819
|
|
36
37
|
argus/backend/plugins/sirenada/model.py,sha256=npKs6GurFXgddOVEiD5hmPwCPi7RPAi9HFs-3s99C8w,4661
|
|
37
38
|
argus/backend/plugins/sirenada/plugin.py,sha256=AlQAakwy3u-OqAqqK3RonUR5oDm-JoiwBUDUF3YEVP4,447
|
|
38
39
|
argus/backend/plugins/sirenada/types.py,sha256=Gm3XMK9YJoozVaeM9XE7n8iRxA6PKBrS23Mo2vJfdLs,697
|
|
39
40
|
argus/backend/service/admin.py,sha256=_VnWl3CkZBOAie_pPbd9sbXZUpBf2SApyNoFZLfB_QI,637
|
|
40
41
|
argus/backend/service/argus_service.py,sha256=3mASrrGXMoZ26zSNkUPpqR84HH9RCI3m9Ag3LV7idPc,24127
|
|
41
|
-
argus/backend/service/build_system_monitor.py,sha256=
|
|
42
|
+
argus/backend/service/build_system_monitor.py,sha256=_Bxugk4CCh2z-I-i74QMPe7M8j0dmBMDri2dd3WGVew,7328
|
|
42
43
|
argus/backend/service/client_service.py,sha256=_eH-sq8507oKXXtMFDAg2kUc445qQucr2fC6pKbmjZc,2290
|
|
44
|
+
argus/backend/service/event_service.py,sha256=iYeqxN2QCYTjYB1WPPv4BEFLXG0Oz3TvskkaK4v9pVY,654
|
|
43
45
|
argus/backend/service/notification_manager.py,sha256=h00Ej_-hH9H7pq0wah_1TH8dnpPyPNsgVJNO1rwJi7o,7011
|
|
44
46
|
argus/backend/service/release_manager.py,sha256=v2yg45oWBpK_uwqkHtJHPTPbe21_cJu3skmzeaChVhw,6259
|
|
45
|
-
argus/backend/service/stats.py,sha256=
|
|
47
|
+
argus/backend/service/stats.py,sha256=FjVmpHTNwRFOC79pzu8gz49yM8lUwcroTfjzCq0laiY,14102
|
|
46
48
|
argus/backend/service/team_manager_service.py,sha256=zY5dvy3ffvQbJuXBvlWKE5dS5LQ3ss6tkFE-cwFZsdw,3010
|
|
47
|
-
argus/backend/service/testrun.py,sha256=
|
|
49
|
+
argus/backend/service/testrun.py,sha256=p0vRGkQYEsDxWzi0JLH-l8ijDyDcmMNeallMk5VrOds,19699
|
|
48
50
|
argus/backend/service/user.py,sha256=N3t43rgKMnSsPXU5R9bigEEGbPjYrc6MsJtof3z7kDE,9027
|
|
49
51
|
argus/backend/template_filters.py,sha256=hD8eDyBYp-X6JVoyQhM-TWYv3MuyorAv0Emz728iGcU,523
|
|
50
52
|
argus/backend/util/common.py,sha256=fmE9cKnJ1CX_Cz4Mw1d_M-T-jwPh8DkKr_fLAx8LY9M,1458
|
|
51
53
|
argus/backend/util/config.py,sha256=Sm0LCRkabYaSUkXNPglyjMr45GCDBNXqJLkmB_s51f0,860
|
|
52
|
-
argus/backend/util/encoders.py,sha256=
|
|
54
|
+
argus/backend/util/encoders.py,sha256=xL4nwA6ByhIo4Zv8mFT0WP-KkVAQ5Cv3SSb-TISpfu8,649
|
|
53
55
|
argus/backend/util/enums.py,sha256=ASL-mqhEtjBKAB-5YDSvGXe7amKjiVv0Q-Gf6eJNbUM,719
|
|
54
56
|
argus/backend/util/logsetup.py,sha256=XWyFLC1_J5G8NcR7oC9l-Pf02ybAvEZR95y5LoY4W48,2438
|
|
55
57
|
argus/backend/util/module_loaders.py,sha256=AcIlX-VRmUQ2THFKT8DLefLSE62Eub2hCxIosg3WgE0,698
|
|
56
58
|
argus/backend/util/send_email.py,sha256=Bb2Hta7WlBCvDKga0_WPFWgxWJEfKpectOGypgf9xzo,3217
|
|
57
59
|
argus/client/__init__.py,sha256=bO9_j5_jK5kvTHR46KEZ0Y-p0li7CBW8QSd-K5Ez4vA,42
|
|
58
60
|
argus/client/base.py,sha256=WHYDUSFhhvAyzU3pKPohlsKifxwifx0vWA42VoAha9M,6676
|
|
59
|
-
argus/client/driver_matrix_tests/client.py,sha256=
|
|
60
|
-
argus/client/sct/client.py,sha256=
|
|
61
|
+
argus/client/driver_matrix_tests/client.py,sha256=3LIS2hwZ3wVKtQDEMiQGBsIoIcpKi29chheF_yRM704,7800
|
|
62
|
+
argus/client/sct/client.py,sha256=GFBRCcwDeLaSvmC6Y6d5DA0prFbk7QOyGl4bvNAz0E8,9946
|
|
61
63
|
argus/client/sct/types.py,sha256=VLgVe7qPmJtCLqtPnuX8N8kMKZq-iY3SKz68nvU6nJ4,371
|
|
62
64
|
argus/client/sirenada/client.py,sha256=ilcyLXJb-0gKbmb9WSPr-Yvldh73joGBhRDoilQoSJ4,6220
|
|
63
65
|
argus/db/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -68,7 +70,7 @@ argus/db/db_types.py,sha256=iLbmrUaDzrBw0kDCnvW0FSZ9-kNc3uQY-fsbIPymV4E,3612
|
|
|
68
70
|
argus/db/interface.py,sha256=HroyA1Yijz5cXLdYbxorHCEu0GH9VeMMqB36IHTlcew,17146
|
|
69
71
|
argus/db/testrun.py,sha256=0YG7FIH5FLQeNlYULxC6rhhyru2rziSMe3qKtYzTBnc,26014
|
|
70
72
|
argus/db/utils.py,sha256=YAWsuLjUScSgKgdaL5aF4Sgr13gqH29Mb5cLctX4V_w,337
|
|
71
|
-
argus_alm-0.11.
|
|
72
|
-
argus_alm-0.11.
|
|
73
|
-
argus_alm-0.11.
|
|
74
|
-
argus_alm-0.11.
|
|
73
|
+
argus_alm-0.11.6.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
74
|
+
argus_alm-0.11.6.dist-info/METADATA,sha256=IvmrSAERJd5cqZ2Dq-1fgMqNl-LyVbXQr7iLUJuzr_I,6824
|
|
75
|
+
argus_alm-0.11.6.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
|
|
76
|
+
argus_alm-0.11.6.dist-info/RECORD,,
|
|
File without changes
|