argus-alm 0.12.3__py3-none-any.whl → 0.12.4b1__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/controller/admin_api.py +26 -0
- argus/backend/controller/api.py +26 -1
- argus/backend/controller/main.py +21 -0
- argus/backend/controller/testrun_api.py +16 -0
- argus/backend/controller/view_api.py +162 -0
- argus/backend/models/web.py +16 -0
- argus/backend/plugins/core.py +25 -10
- argus/backend/plugins/driver_matrix_tests/controller.py +39 -0
- argus/backend/plugins/driver_matrix_tests/model.py +251 -3
- argus/backend/plugins/driver_matrix_tests/raw_types.py +27 -0
- argus/backend/plugins/driver_matrix_tests/service.py +18 -0
- argus/backend/plugins/driver_matrix_tests/udt.py +14 -13
- argus/backend/plugins/generic/model.py +5 -2
- argus/backend/plugins/sct/service.py +13 -1
- argus/backend/service/argus_service.py +116 -20
- argus/backend/service/build_system_monitor.py +37 -7
- argus/backend/service/jenkins_service.py +2 -1
- argus/backend/service/release_manager.py +14 -0
- argus/backend/service/stats.py +147 -11
- argus/backend/service/testrun.py +44 -5
- argus/backend/service/views.py +258 -0
- argus/backend/template_filters.py +7 -0
- argus/backend/util/common.py +14 -2
- argus/client/driver_matrix_tests/cli.py +110 -0
- argus/client/driver_matrix_tests/client.py +56 -193
- argus_alm-0.12.4b1.dist-info/METADATA +129 -0
- {argus_alm-0.12.3.dist-info → argus_alm-0.12.4b1.dist-info}/RECORD +30 -27
- {argus_alm-0.12.3.dist-info → argus_alm-0.12.4b1.dist-info}/entry_points.txt +1 -0
- argus_alm-0.12.3.dist-info/METADATA +0 -207
- {argus_alm-0.12.3.dist-info → argus_alm-0.12.4b1.dist-info}/LICENSE +0 -0
- {argus_alm-0.12.3.dist-info → argus_alm-0.12.4b1.dist-info}/WHEEL +0 -0
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from datetime import datetime
|
|
3
3
|
from functools import reduce
|
|
4
|
+
import logging
|
|
5
|
+
from pprint import pformat
|
|
4
6
|
import re
|
|
7
|
+
from typing import Literal, TypedDict
|
|
5
8
|
from uuid import UUID
|
|
9
|
+
from xml.etree import ElementTree
|
|
6
10
|
from cassandra.cqlengine import columns
|
|
7
11
|
from argus.backend.db import ScyllaCluster
|
|
8
12
|
from argus.backend.models.web import ArgusRelease
|
|
@@ -12,6 +16,8 @@ from argus.backend.plugins.driver_matrix_tests.raw_types import RawMatrixTestRes
|
|
|
12
16
|
from argus.backend.util.enums import TestStatus
|
|
13
17
|
|
|
14
18
|
|
|
19
|
+
LOGGER = logging.getLogger(__name__)
|
|
20
|
+
|
|
15
21
|
class DriverMatrixPluginError(Exception):
|
|
16
22
|
pass
|
|
17
23
|
|
|
@@ -26,6 +32,65 @@ class DriverMatrixRunSubmissionRequest():
|
|
|
26
32
|
matrix_results: list[RawMatrixTestResult]
|
|
27
33
|
|
|
28
34
|
|
|
35
|
+
@dataclass(init=True, repr=True, frozen=True)
|
|
36
|
+
class DriverMatrixRunSubmissionRequestV2():
|
|
37
|
+
schema_version: str
|
|
38
|
+
run_id: str
|
|
39
|
+
job_name: str
|
|
40
|
+
job_url: str
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
TestTypeType = Literal['java', 'cpp', 'python', 'gocql']
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class AdaptedXUnitData(TypedDict):
|
|
47
|
+
timestamp: str
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def python_driver_matrix_adapter(xml: ElementTree.ElementTree) -> AdaptedXUnitData:
|
|
51
|
+
testsuites = list(xml.getroot().iter("testsuite"))
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
"timestamp": testsuites[0].attrib.get("timestamp"),
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def java_driver_matrix_adapter(xml: ElementTree.ElementTree) -> AdaptedXUnitData:
|
|
59
|
+
testsuites = xml.getroot()
|
|
60
|
+
ts_now = datetime.utcnow().timestamp()
|
|
61
|
+
try:
|
|
62
|
+
time_taken = float(testsuites.attrib.get("time"))
|
|
63
|
+
except ValueError:
|
|
64
|
+
time_taken = 0.0
|
|
65
|
+
|
|
66
|
+
timestamp = datetime.utcfromtimestamp(ts_now - time_taken).isoformat()
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
"timestamp": timestamp,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def cpp_driver_matrix_adapter(xml: ElementTree.ElementTree) -> AdaptedXUnitData:
|
|
74
|
+
testsuites = xml.getroot()
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
"timestamp": testsuites.attrib.get("timestamp"),
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def gocql_driver_matrix_adapter(xml: ElementTree.ElementTree) -> AdaptedXUnitData:
|
|
82
|
+
testsuites = list(xml.getroot().iter("testsuite"))
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
"timestamp": testsuites[0].attrib.get("timestamp"),
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def generic_adapter(xml: ElementTree.ElementTree) -> AdaptedXUnitData:
|
|
90
|
+
return {
|
|
91
|
+
"timestamp": datetime.utcnow().isoformat()
|
|
92
|
+
}
|
|
93
|
+
|
|
29
94
|
class DriverTestRun(PluginModelBase):
|
|
30
95
|
_plugin_name = "driver-matrix-tests"
|
|
31
96
|
__table_name__ = "driver_test_run"
|
|
@@ -33,12 +98,22 @@ class DriverTestRun(PluginModelBase):
|
|
|
33
98
|
test_collection = columns.List(value_type=columns.UserDefinedType(user_type=TestCollection))
|
|
34
99
|
environment_info = columns.List(value_type=columns.UserDefinedType(user_type=EnvironmentInfo))
|
|
35
100
|
|
|
101
|
+
_no_upstream = ["rust"]
|
|
102
|
+
|
|
103
|
+
_TEST_ADAPTERS = {
|
|
104
|
+
"java": java_driver_matrix_adapter,
|
|
105
|
+
"cpp": cpp_driver_matrix_adapter,
|
|
106
|
+
"python": python_driver_matrix_adapter,
|
|
107
|
+
"gocql": gocql_driver_matrix_adapter,
|
|
108
|
+
}
|
|
109
|
+
|
|
36
110
|
|
|
37
111
|
_artifact_fnames = {
|
|
38
112
|
"cpp": r"TEST-(?P<driver_name>[\w]*)-(?P<version>[\d\.-]*)",
|
|
39
113
|
"gocql": r"xunit\.(?P<driver_name>[\w]*)\.(?P<proto>v\d)\.(?P<version>[v\d\.]*)",
|
|
40
114
|
"python": r"pytest\.(?P<driver_name>[\w]*)\.(?P<proto>v\d)\.(?P<version>[\d\.]*)",
|
|
41
115
|
"java": r"TEST-(?P<version>[\d\.\w-]*)",
|
|
116
|
+
"rust": r"(?P<driver_name>rust)_results_v(?P<version>[\d\w\-.]*)",
|
|
42
117
|
}
|
|
43
118
|
|
|
44
119
|
@classmethod
|
|
@@ -76,6 +151,173 @@ class DriverTestRun(PluginModelBase):
|
|
|
76
151
|
|
|
77
152
|
@classmethod
|
|
78
153
|
def submit_run(cls, request_data: dict) -> 'DriverTestRun':
|
|
154
|
+
if request_data["schema_version"] == "v2":
|
|
155
|
+
req = DriverMatrixRunSubmissionRequestV2(**request_data)
|
|
156
|
+
else:
|
|
157
|
+
return cls.submit_matrix_run(request_data)
|
|
158
|
+
|
|
159
|
+
run = cls()
|
|
160
|
+
run.id = req.run_id
|
|
161
|
+
run.build_id = req.job_name
|
|
162
|
+
run.build_job_url = req.job_url
|
|
163
|
+
run.start_time = datetime.utcnow()
|
|
164
|
+
run.assign_categories()
|
|
165
|
+
try:
|
|
166
|
+
run.assignee = run.get_scheduled_assignee()
|
|
167
|
+
except Exception: # pylint: disable=broad-except
|
|
168
|
+
run.assignee = None
|
|
169
|
+
|
|
170
|
+
run.status = TestStatus.CREATED.value
|
|
171
|
+
run.save()
|
|
172
|
+
return run
|
|
173
|
+
|
|
174
|
+
@classmethod
|
|
175
|
+
def submit_driver_result(cls, run_id: UUID, driver_name: str, driver_type: TestTypeType, xml_data: str):
|
|
176
|
+
run: DriverTestRun = cls.get(id=run_id)
|
|
177
|
+
|
|
178
|
+
collection = run.parse_result_xml(driver_name, xml_data, driver_type)
|
|
179
|
+
run.test_collection.append(collection)
|
|
180
|
+
|
|
181
|
+
if run.status == TestStatus.CREATED:
|
|
182
|
+
run.status = TestStatus.RUNNING.value
|
|
183
|
+
|
|
184
|
+
run.save()
|
|
185
|
+
return run
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@classmethod
|
|
189
|
+
def submit_driver_failure(cls, run_id: UUID, driver_name: str, driver_type: TestTypeType, fail_message: str):
|
|
190
|
+
run: DriverTestRun = cls.get(id=run_id)
|
|
191
|
+
|
|
192
|
+
collection = TestCollection()
|
|
193
|
+
collection.failures = 1
|
|
194
|
+
collection.failure_message = fail_message
|
|
195
|
+
collection.name = driver_name
|
|
196
|
+
driver_info = run.get_driver_info(driver_name, driver_type)
|
|
197
|
+
collection.driver = driver_info.get("driver_name")
|
|
198
|
+
collection.tests_total = 1
|
|
199
|
+
run.test_collection.append(collection)
|
|
200
|
+
|
|
201
|
+
if run.status == TestStatus.CREATED:
|
|
202
|
+
run.status = TestStatus.RUNNING.value
|
|
203
|
+
|
|
204
|
+
run.save()
|
|
205
|
+
return run
|
|
206
|
+
|
|
207
|
+
@classmethod
|
|
208
|
+
def submit_env_info(cls, run_id: UUID, env_data: str):
|
|
209
|
+
run: DriverTestRun = cls.get(id=run_id)
|
|
210
|
+
env = run.parse_build_environment(env_data)
|
|
211
|
+
|
|
212
|
+
for key, value in env.items():
|
|
213
|
+
env_info = EnvironmentInfo()
|
|
214
|
+
env_info.key = key
|
|
215
|
+
env_info.value = value
|
|
216
|
+
run.environment_info.append(env_info)
|
|
217
|
+
|
|
218
|
+
run.scylla_version = env.get("scylla-version")
|
|
219
|
+
|
|
220
|
+
run.save()
|
|
221
|
+
return run
|
|
222
|
+
|
|
223
|
+
def parse_build_environment(self, raw_env: str) -> dict[str, str]:
|
|
224
|
+
result = {}
|
|
225
|
+
for line in raw_env.split("\n"):
|
|
226
|
+
if not line:
|
|
227
|
+
continue
|
|
228
|
+
LOGGER.debug("ENV: %s", line)
|
|
229
|
+
key, val = line.split(": ")
|
|
230
|
+
result[key] = val.strip()
|
|
231
|
+
|
|
232
|
+
return result
|
|
233
|
+
|
|
234
|
+
def get_test_cases(self, cases: list[ElementTree.Element]) -> list[TestCase]:
|
|
235
|
+
result = []
|
|
236
|
+
for raw_case in cases:
|
|
237
|
+
children = list(raw_case.findall("./*"))
|
|
238
|
+
if len(children) > 0:
|
|
239
|
+
status = children[0].tag
|
|
240
|
+
message = f"{children[0].attrib.get('message', 'no-message')} ({children[0].attrib.get('type', 'no-type')})"
|
|
241
|
+
else:
|
|
242
|
+
status = "passed"
|
|
243
|
+
message = ""
|
|
244
|
+
|
|
245
|
+
case = TestCase()
|
|
246
|
+
case.name = raw_case.attrib["name"]
|
|
247
|
+
case.status = status
|
|
248
|
+
case.time = float(raw_case.attrib.get("time", 0.0))
|
|
249
|
+
case.classname = raw_case.attrib.get("classname", "")
|
|
250
|
+
case.message = message
|
|
251
|
+
result.append(case)
|
|
252
|
+
|
|
253
|
+
return result
|
|
254
|
+
|
|
255
|
+
def get_driver_info(self, xml_name: str, test_type: TestTypeType) -> dict[str, str]:
|
|
256
|
+
if test_type == "cpp":
|
|
257
|
+
filename_re = r"TEST-(?P<driver_name>[\w]*)-(?P<version>[\d\.]*)(\.xml)?"
|
|
258
|
+
else:
|
|
259
|
+
filename_re = r"(?P<name>[\w]*)\.(?P<driver_name>[\w]*)\.(?P<proto>v\d)\.(?P<version>[\d\.]*)(\.xml)?"
|
|
260
|
+
|
|
261
|
+
match = re.match(filename_re, xml_name)
|
|
262
|
+
|
|
263
|
+
return match.groupdict() if match else {}
|
|
264
|
+
|
|
265
|
+
def get_passed_count(self, suite_attribs: dict[str, str]) -> int:
|
|
266
|
+
if (pass_count := suite_attribs.get("passed")):
|
|
267
|
+
return int(pass_count)
|
|
268
|
+
total = int(suite_attribs.get("tests", 0))
|
|
269
|
+
errors = int(suite_attribs.get("errors", 0))
|
|
270
|
+
skipped = int(suite_attribs.get("skipped", 0))
|
|
271
|
+
failures = int(suite_attribs.get("failures", 0))
|
|
272
|
+
|
|
273
|
+
return total - errors - skipped - failures
|
|
274
|
+
|
|
275
|
+
def parse_result_xml(self, name: str, xml_data: str, test_type: TestTypeType) -> TestCollection:
|
|
276
|
+
xml: ElementTree.ElementTree = ElementTree.ElementTree(ElementTree.fromstring(xml_data))
|
|
277
|
+
LOGGER.debug("%s", pformat(xml))
|
|
278
|
+
testsuites = xml.getroot()
|
|
279
|
+
adapted_data = self._TEST_ADAPTERS.get(test_type, generic_adapter)(xml)
|
|
280
|
+
|
|
281
|
+
driver_info = self.get_driver_info(name, test_type)
|
|
282
|
+
test_collection = TestCollection()
|
|
283
|
+
test_collection.timestamp = datetime.fromisoformat(adapted_data["timestamp"][0:-1] if adapted_data["timestamp"][-1] == "Z" else adapted_data["timestamp"])
|
|
284
|
+
test_collection.name = name
|
|
285
|
+
test_collection.driver = driver_info.get("driver_name")
|
|
286
|
+
test_collection.tests_total = 0
|
|
287
|
+
test_collection.failures = 0
|
|
288
|
+
test_collection.errors = 0
|
|
289
|
+
test_collection.disabled = 0
|
|
290
|
+
test_collection.skipped = 0
|
|
291
|
+
test_collection.passed = 0
|
|
292
|
+
test_collection.time = 0.0
|
|
293
|
+
test_collection.suites = []
|
|
294
|
+
|
|
295
|
+
for xml_suite in testsuites.iter("testsuite"):
|
|
296
|
+
suite = TestSuite()
|
|
297
|
+
suite.name = xml_suite.attrib["name"]
|
|
298
|
+
suite.tests_total = int(xml_suite.attrib.get("tests", 0))
|
|
299
|
+
suite.failures = int(xml_suite.attrib.get("failures", 0))
|
|
300
|
+
suite.disabled = int(0)
|
|
301
|
+
suite.passed = self.get_passed_count(xml_suite.attrib)
|
|
302
|
+
suite.skipped = int(xml_suite.attrib.get("skipped", 0))
|
|
303
|
+
suite.errors = int(xml_suite.attrib.get("errors", 0))
|
|
304
|
+
suite.time = float(xml_suite.attrib["time"])
|
|
305
|
+
suite.cases = self.get_test_cases(xml_suite.findall("testcase"))
|
|
306
|
+
|
|
307
|
+
test_collection.suites.append(suite)
|
|
308
|
+
test_collection.tests_total += suite.tests_total
|
|
309
|
+
test_collection.failures += suite.failures
|
|
310
|
+
test_collection.errors += suite.errors
|
|
311
|
+
test_collection.disabled += suite.disabled
|
|
312
|
+
test_collection.skipped += suite.skipped
|
|
313
|
+
test_collection.passed += suite.passed
|
|
314
|
+
test_collection.time += suite.time
|
|
315
|
+
|
|
316
|
+
return test_collection
|
|
317
|
+
|
|
318
|
+
@classmethod
|
|
319
|
+
def submit_matrix_run(cls, request_data):
|
|
320
|
+
# Legacy method
|
|
79
321
|
req = DriverMatrixRunSubmissionRequest(**request_data)
|
|
80
322
|
run = cls()
|
|
81
323
|
run.id = req.run_id # pylint: disable=invalid-name
|
|
@@ -144,21 +386,26 @@ class DriverTestRun(PluginModelBase):
|
|
|
144
386
|
return []
|
|
145
387
|
|
|
146
388
|
def _determine_run_status(self):
|
|
389
|
+
for collection in self.test_collection:
|
|
390
|
+
# patch failure
|
|
391
|
+
if collection.failure_message:
|
|
392
|
+
return TestStatus.FAILED
|
|
393
|
+
|
|
147
394
|
if len(self.test_collection) < 2:
|
|
148
395
|
return TestStatus.FAILED
|
|
149
396
|
|
|
150
397
|
driver_types = {collection.driver for collection in self.test_collection}
|
|
151
|
-
if len(driver_types) <= 1:
|
|
398
|
+
if len(driver_types) <= 1 and not any(driver for driver in self._no_upstream if driver in driver_types):
|
|
152
399
|
return TestStatus.FAILED
|
|
153
400
|
|
|
154
|
-
failure_count = reduce(lambda acc, val: acc + (val.failures + val.errors), self.test_collection, 0)
|
|
401
|
+
failure_count = reduce(lambda acc, val: acc + (val.failures or 0 + val.errors or 0), self.test_collection, 0)
|
|
155
402
|
if failure_count > 0:
|
|
156
403
|
return TestStatus.FAILED
|
|
157
404
|
|
|
158
405
|
return TestStatus.PASSED
|
|
159
406
|
|
|
160
407
|
def change_status(self, new_status: TestStatus):
|
|
161
|
-
|
|
408
|
+
self.status = new_status
|
|
162
409
|
|
|
163
410
|
def get_events(self) -> list:
|
|
164
411
|
return []
|
|
@@ -168,6 +415,7 @@ class DriverTestRun(PluginModelBase):
|
|
|
168
415
|
|
|
169
416
|
def finish_run(self, payload: dict = None):
|
|
170
417
|
self.end_time = datetime.utcnow()
|
|
418
|
+
self.status = self._determine_run_status().value
|
|
171
419
|
|
|
172
420
|
def submit_logs(self, logs: list[dict]):
|
|
173
421
|
pass
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
1
2
|
from typing import TypedDict
|
|
3
|
+
from uuid import UUID
|
|
2
4
|
|
|
3
5
|
|
|
4
6
|
class RawMatrixTestCase(TypedDict):
|
|
@@ -33,3 +35,28 @@ class RawMatrixTestResult(TypedDict):
|
|
|
33
35
|
time: float
|
|
34
36
|
timestamp: str
|
|
35
37
|
suites: list[RawMatrixTestSuite]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(init=True, frozen=True)
|
|
41
|
+
class DriverMatrixSubmitResultRequest():
|
|
42
|
+
schema_version: str
|
|
43
|
+
run_id: UUID
|
|
44
|
+
driver_type: str
|
|
45
|
+
driver_name: str
|
|
46
|
+
raw_xml: str
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass(init=True, frozen=True)
|
|
50
|
+
class DriverMatrixSubmitFailureRequest():
|
|
51
|
+
schema_version: str
|
|
52
|
+
run_id: UUID
|
|
53
|
+
driver_type: str
|
|
54
|
+
driver_name: str
|
|
55
|
+
failure_reason: str
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass(init=True, frozen=True)
|
|
59
|
+
class DriverMatrixSubmitEnvRequest():
|
|
60
|
+
schema_version: str
|
|
61
|
+
run_id: UUID
|
|
62
|
+
raw_env: str
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import logging
|
|
3
|
+
from uuid import UUID
|
|
1
4
|
from argus.backend.db import ScyllaCluster
|
|
2
5
|
from argus.backend.models.web import ArgusRelease, ArgusTest
|
|
3
6
|
from argus.backend.plugins.driver_matrix_tests.model import DriverTestRun
|
|
4
7
|
|
|
5
8
|
|
|
9
|
+
LOGGER = logging.getLogger(__name__)
|
|
10
|
+
|
|
6
11
|
class DriverMatrixService:
|
|
7
12
|
def tested_versions_report(self, build_id: str) -> dict:
|
|
8
13
|
db = ScyllaCluster.get()
|
|
@@ -40,3 +45,16 @@ class DriverMatrixService:
|
|
|
40
45
|
"versions": version_map,
|
|
41
46
|
}
|
|
42
47
|
return response
|
|
48
|
+
|
|
49
|
+
def submit_driver_result(self, run_id: UUID | str, driver_name: str, driver_type: str, raw_xml: str) -> bool:
|
|
50
|
+
xml_data = base64.decodebytes(bytes(raw_xml, encoding="utf-8"))
|
|
51
|
+
DriverTestRun.submit_driver_result(UUID(run_id), driver_name, driver_type, xml_data)
|
|
52
|
+
return True
|
|
53
|
+
|
|
54
|
+
def submit_driver_failure(self, run_id: UUID | str, driver_name: str, driver_type: str, failure_reason: str) -> bool:
|
|
55
|
+
DriverTestRun.submit_driver_failure(UUID(run_id), driver_name, driver_type, failure_reason)
|
|
56
|
+
return True
|
|
57
|
+
|
|
58
|
+
def submit_env_info(self, run_id: UUID | str, raw_env: str) -> bool:
|
|
59
|
+
DriverTestRun.submit_env_info(UUID(run_id), raw_env)
|
|
60
|
+
return True
|
|
@@ -12,12 +12,12 @@ class TestCase(UserType):
|
|
|
12
12
|
|
|
13
13
|
class TestSuite(UserType):
|
|
14
14
|
name = columns.Text()
|
|
15
|
-
tests_total = columns.Integer()
|
|
16
|
-
failures = columns.Integer()
|
|
17
|
-
disabled = columns.Integer()
|
|
18
|
-
skipped = columns.Integer()
|
|
19
|
-
passed = columns.Integer()
|
|
20
|
-
errors = columns.Integer()
|
|
15
|
+
tests_total = columns.Integer(default=lambda: 0)
|
|
16
|
+
failures = columns.Integer(default=lambda: 0)
|
|
17
|
+
disabled = columns.Integer(default=lambda: 0)
|
|
18
|
+
skipped = columns.Integer(default=lambda: 0)
|
|
19
|
+
passed = columns.Integer(default=lambda: 0)
|
|
20
|
+
errors = columns.Integer(default=lambda: 0)
|
|
21
21
|
time = columns.Float()
|
|
22
22
|
cases = columns.List(value_type=columns.UserDefinedType(user_type=TestCase))
|
|
23
23
|
|
|
@@ -25,14 +25,15 @@ class TestSuite(UserType):
|
|
|
25
25
|
class TestCollection(UserType):
|
|
26
26
|
name = columns.Text()
|
|
27
27
|
driver = columns.Text()
|
|
28
|
-
tests_total = columns.Integer()
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
tests_total = columns.Integer(default=lambda: 0)
|
|
29
|
+
failure_message = columns.Text()
|
|
30
|
+
failures = columns.Integer(default=lambda: 0)
|
|
31
|
+
disabled = columns.Integer(default=lambda: 0)
|
|
32
|
+
skipped = columns.Integer(default=lambda: 0)
|
|
33
|
+
passed = columns.Integer(default=lambda: 0)
|
|
34
|
+
errors = columns.Integer(default=lambda: 0)
|
|
34
35
|
timestamp = columns.DateTime()
|
|
35
|
-
time = columns.Float()
|
|
36
|
+
time = columns.Float(default=lambda: 0.0)
|
|
36
37
|
suites = columns.List(value_type=columns.UserDefinedType(user_type=TestSuite))
|
|
37
38
|
|
|
38
39
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
+
import re
|
|
2
3
|
from uuid import UUID
|
|
3
4
|
from cassandra.cqlengine import columns
|
|
4
5
|
from cassandra.cqlengine.models import Model
|
|
@@ -37,8 +38,10 @@ class GenericRun(PluginModelBase):
|
|
|
37
38
|
return sorted(list(unique_versions), reverse=True)
|
|
38
39
|
|
|
39
40
|
def submit_product_version(self, version: str):
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
pattern = re.compile(r"((?P<short>[\w.~]+)-(?P<build>(0\.)?(?P<date>[0-9]{8,8})\.(?P<commit>\w+).*))")
|
|
42
|
+
if match := pattern.search(version):
|
|
43
|
+
self.scylla_version = match.group("short")
|
|
44
|
+
self.set_full_version(version)
|
|
42
45
|
|
|
43
46
|
@classmethod
|
|
44
47
|
def load_test_run(cls, run_id: UUID) -> 'GenericRun':
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import base64
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
+
from datetime import datetime
|
|
3
4
|
from functools import reduce
|
|
4
5
|
import logging
|
|
5
6
|
import math
|
|
@@ -397,14 +398,25 @@ class SCTService:
|
|
|
397
398
|
coredump_events = filter(lambda v: "coredumpevent" in v.lower(), flat_messages)
|
|
398
399
|
for idx, event in enumerate(coredump_events):
|
|
399
400
|
core_pattern = r"corefile_url=(?P<url>.+)$"
|
|
401
|
+
ts_pattern = r"^(?P<ts>\d{4}-\d{2}-\d{2} ([\d:]*)\.\d{3})"
|
|
400
402
|
node_name_pattern = r"node=(?P<name>.+)$"
|
|
401
403
|
core_url_match = re.search(core_pattern, event, re.MULTILINE)
|
|
402
404
|
node_name_match = re.search(node_name_pattern, event, re.MULTILINE)
|
|
405
|
+
ts_match = re.search(ts_pattern, event)
|
|
403
406
|
if core_url_match:
|
|
404
407
|
node_name = node_name_match.group("name") if node_name_match else f"unknown-node-{idx}"
|
|
408
|
+
split_name = node_name.split(" ")
|
|
409
|
+
node_name = split_name[1] if len(split_name) >= 2 else node_name
|
|
405
410
|
url = core_url_match.group("url")
|
|
411
|
+
timestamp_component = ""
|
|
412
|
+
if ts_match:
|
|
413
|
+
try:
|
|
414
|
+
timestamp = datetime.fromisoformat(ts_match.group("ts"))
|
|
415
|
+
timestamp_component = timestamp.strftime("-%Y-%m-%d_%H-%M-%S")
|
|
416
|
+
except ValueError:
|
|
417
|
+
pass
|
|
406
418
|
log_link = {
|
|
407
|
-
"log_name": f"
|
|
419
|
+
"log_name": f"core.scylla-{node_name}{timestamp_component}.gz",
|
|
408
420
|
"log_link": url
|
|
409
421
|
}
|
|
410
422
|
links.append(log_link)
|