argus-alm 0.12.4b2__tar.gz → 0.12.5__tar.gz

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.
Files changed (92) hide show
  1. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/PKG-INFO +1 -1
  2. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/controller/client_api.py +11 -0
  3. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/controller/testrun_api.py +11 -0
  4. argus_alm-0.12.5/argus/backend/models/result.py +41 -0
  5. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/models/web.py +4 -0
  6. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/plugins/core.py +4 -2
  7. argus_alm-0.12.5/argus/backend/plugins/driver_matrix_tests/controller.py +24 -0
  8. argus_alm-0.12.5/argus/backend/plugins/driver_matrix_tests/model.py +175 -0
  9. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/plugins/driver_matrix_tests/raw_types.py +0 -27
  10. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/plugins/driver_matrix_tests/service.py +0 -18
  11. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/plugins/driver_matrix_tests/udt.py +13 -14
  12. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/plugins/sct/testrun.py +7 -0
  13. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/service/client_service.py +18 -0
  14. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/service/testrun.py +19 -0
  15. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/client/base.py +16 -0
  16. argus_alm-0.12.5/argus/client/driver_matrix_tests/client.py +216 -0
  17. argus_alm-0.12.5/argus/client/generic_result.py +99 -0
  18. argus_alm-0.12.5/argus/client/generic_result_old.py +143 -0
  19. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/pyproject.toml +1 -2
  20. argus_alm-0.12.4b2/argus/backend/plugins/driver_matrix_tests/controller.py +0 -63
  21. argus_alm-0.12.4b2/argus/backend/plugins/driver_matrix_tests/model.py +0 -421
  22. argus_alm-0.12.4b2/argus/client/driver_matrix_tests/cli.py +0 -110
  23. argus_alm-0.12.4b2/argus/client/driver_matrix_tests/client.py +0 -79
  24. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/LICENSE +0 -0
  25. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/README.md +0 -0
  26. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/__init__.py +0 -0
  27. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/.gitkeep +0 -0
  28. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/__init__.py +0 -0
  29. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/cli.py +0 -0
  30. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/controller/__init__.py +0 -0
  31. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/controller/admin.py +0 -0
  32. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/controller/admin_api.py +0 -0
  33. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/controller/api.py +0 -0
  34. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/controller/auth.py +0 -0
  35. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/controller/main.py +0 -0
  36. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/controller/notification_api.py +0 -0
  37. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/controller/notifications.py +0 -0
  38. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/controller/team.py +0 -0
  39. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/controller/team_ui.py +0 -0
  40. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/controller/view_api.py +0 -0
  41. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/db.py +0 -0
  42. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/error_handlers.py +0 -0
  43. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/events/event_processors.py +0 -0
  44. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/models/__init__.py +0 -0
  45. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/plugins/__init__.py +0 -0
  46. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/plugins/driver_matrix_tests/plugin.py +0 -0
  47. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/plugins/generic/model.py +0 -0
  48. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/plugins/generic/plugin.py +0 -0
  49. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/plugins/generic/types.py +0 -0
  50. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/plugins/loader.py +0 -0
  51. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/plugins/sct/controller.py +0 -0
  52. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/plugins/sct/plugin.py +0 -0
  53. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/plugins/sct/resource_setup.py +0 -0
  54. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/plugins/sct/service.py +0 -0
  55. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/plugins/sct/types.py +0 -0
  56. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/plugins/sct/udt.py +0 -0
  57. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/plugins/sirenada/model.py +0 -0
  58. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/plugins/sirenada/plugin.py +0 -0
  59. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/plugins/sirenada/types.py +0 -0
  60. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/service/admin.py +0 -0
  61. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/service/argus_service.py +0 -0
  62. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/service/build_system_monitor.py +0 -0
  63. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/service/event_service.py +0 -0
  64. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/service/jenkins_service.py +0 -0
  65. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/service/notification_manager.py +0 -0
  66. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/service/release_manager.py +0 -0
  67. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/service/stats.py +0 -0
  68. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/service/team_manager_service.py +0 -0
  69. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/service/user.py +0 -0
  70. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/service/views.py +0 -0
  71. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/template_filters.py +0 -0
  72. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/util/common.py +0 -0
  73. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/util/config.py +0 -0
  74. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/util/encoders.py +0 -0
  75. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/util/enums.py +0 -0
  76. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/util/logsetup.py +0 -0
  77. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/util/module_loaders.py +0 -0
  78. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/backend/util/send_email.py +0 -0
  79. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/client/__init__.py +0 -0
  80. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/client/generic/cli.py +0 -0
  81. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/client/generic/client.py +0 -0
  82. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/client/sct/client.py +0 -0
  83. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/client/sct/types.py +0 -0
  84. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/client/sirenada/client.py +0 -0
  85. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/db/.gitkeep +0 -0
  86. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/db/argus_json.py +0 -0
  87. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/db/cloud_types.py +0 -0
  88. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/db/config.py +0 -0
  89. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/db/db_types.py +0 -0
  90. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/db/interface.py +0 -0
  91. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/db/testrun.py +0 -0
  92. {argus_alm-0.12.4b2 → argus_alm-0.12.5}/argus/db/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: argus-alm
3
- Version: 0.12.4b2
3
+ Version: 0.12.5
4
4
  Summary: Argus
5
5
  Home-page: https://github.com/scylladb/argus
6
6
  License: Apache-2.0
@@ -90,3 +90,14 @@ def run_finalize(run_type: str, run_id: str):
90
90
  "status": "ok",
91
91
  "response": result
92
92
  }
93
+
94
+
95
+ @bp.route("/testrun/<string:run_type>/<string:run_id>/submit_results", methods=["POST"])
96
+ @api_login_required
97
+ def submit_results(run_type: str, run_id: str):
98
+ payload = get_payload(request)
99
+ result = ClientService().submit_results(run_type=run_type, run_id=run_id, results=payload)
100
+ return {
101
+ "status": "ok",
102
+ "response": result
103
+ }
@@ -63,6 +63,17 @@ def test_run_activity(run_id: str):
63
63
  }
64
64
 
65
65
 
66
+
67
+ @bp.route("/run/<string:test_id>/<string:run_id>/fetch_results", methods=["GET"])
68
+ @api_login_required
69
+ def fetch_results(test_id: str, run_id: str):
70
+ tables = TestRunService().fetch_results(test_id=UUID(test_id), run_id=UUID(run_id))
71
+ return {
72
+ "status": "ok",
73
+ "tables": tables
74
+ }
75
+
76
+
66
77
  @bp.route("/test/<string:test_id>/run/<string:run_id>/status/set", methods=["POST"])
67
78
  @api_login_required
68
79
  def set_testrun_status(test_id: str, run_id: str):
@@ -0,0 +1,41 @@
1
+ from cassandra.cqlengine import columns
2
+ from cassandra.cqlengine.models import Model
3
+ from cassandra.cqlengine.usertype import UserType
4
+ from enum import Enum
5
+
6
+
7
+ class Status(Enum):
8
+ PASS = 0
9
+ WARNING = 1
10
+ ERROR = 2
11
+
12
+
13
+ class ColumnMetadata(UserType):
14
+ name = columns.Ascii()
15
+ unit = columns.Text()
16
+ type = columns.Ascii()
17
+
18
+
19
+ class ArgusGenericResultMetadata(Model):
20
+ __table_name__ = "generic_result_metadata_v1"
21
+ test_id = columns.UUID(partition_key=True)
22
+ name = columns.Text(required=True, primary_key=True)
23
+ description = columns.Text()
24
+ columns_meta = columns.List(value_type=columns.UserDefinedType(ColumnMetadata))
25
+ rows_meta = columns.List(value_type=columns.Ascii())
26
+
27
+ def __init__(self, **kwargs):
28
+ kwargs["columns_meta"] = [ColumnMetadata(**col) for col in kwargs.pop('columns_meta', [])]
29
+ super().__init__(**kwargs)
30
+
31
+
32
+ class ArgusGenericResultData(Model):
33
+ __table_name__ = "generic_result_data_v1"
34
+ test_id = columns.UUID(partition_key=True)
35
+ name = columns.Text(partition_key=True)
36
+ run_id = columns.UUID(primary_key=True)
37
+ column = columns.Ascii(primary_key=True, index=True)
38
+ row = columns.Ascii(primary_key=True, index=True)
39
+ sut_timestamp = columns.DateTime() # for sorting
40
+ value = columns.Double()
41
+ status = columns.Ascii()
@@ -6,6 +6,8 @@ from cassandra.cqlengine.usertype import UserType
6
6
  from cassandra.cqlengine import columns
7
7
  from cassandra.util import uuid_from_time, unix_time_from_uuid1 # pylint: disable=no-name-in-module
8
8
 
9
+ from argus.backend.models.result import ArgusGenericResultMetadata, ArgusGenericResultData
10
+
9
11
 
10
12
  def uuid_now():
11
13
  return uuid_from_time(datetime.utcnow())
@@ -377,6 +379,8 @@ USED_MODELS: list[Model] = [
377
379
  ArgusScheduleAssignee,
378
380
  ArgusScheduleGroup,
379
381
  ArgusScheduleTest,
382
+ ArgusGenericResultMetadata,
383
+ ArgusGenericResultData,
380
384
  ]
381
385
 
382
386
  USED_TYPES: list[UserType] = [
@@ -30,7 +30,7 @@ class PluginModelBase(Model):
30
30
  _plugin_name = "unknown"
31
31
  # Metadata
32
32
  build_id = columns.Text(required=True, partition_key=True)
33
- start_time = columns.DateTime(required=True, primary_key=True, clustering_order="DESC", default=datetime.utcnow, custom_index=True)
33
+ start_time = columns.DateTime(required=True, primary_key=True, clustering_order="DESC", default=datetime.now, custom_index=True)
34
34
  id = columns.UUID(index=True, required=True)
35
35
  release_id = columns.UUID(index=True)
36
36
  group_id = columns.UUID(index=True)
@@ -105,7 +105,7 @@ class PluginModelBase(Model):
105
105
  assignees = ArgusScheduleAssignee.filter(
106
106
  schedule_id=schedule.id
107
107
  ).all()
108
- assignees_uuids.append(*[assignee.assignee for assignee in assignees])
108
+ assignees_uuids.extend([assignee.assignee for assignee in assignees])
109
109
 
110
110
  return assignees_uuids[0] if len(assignees_uuids) > 0 else None
111
111
 
@@ -213,6 +213,8 @@ class PluginModelBase(Model):
213
213
  def finish_run(self, payload: dict = None):
214
214
  raise NotImplementedError()
215
215
 
216
+ def sut_timestamp(self) -> float:
217
+ raise NotImplementedError()
216
218
 
217
219
  class PluginInfoBase:
218
220
  # pylint: disable=too-few-public-methods
@@ -0,0 +1,24 @@
1
+ from flask import Blueprint, request
2
+
3
+ from argus.backend.error_handlers import handle_api_exception
4
+ from argus.backend.service.user import api_login_required
5
+ from argus.backend.plugins.driver_matrix_tests.service import DriverMatrixService
6
+
7
+ bp = Blueprint("driver_matrix_api", __name__, url_prefix="/driver_matrix")
8
+ bp.register_error_handler(Exception, handle_api_exception)
9
+
10
+
11
+ @bp.route("/test_report", methods=["GET"])
12
+ @api_login_required
13
+ def driver_matrix_test_report():
14
+
15
+ build_id = request.args.get("buildId")
16
+ if not build_id:
17
+ raise Exception("No build id provided")
18
+
19
+
20
+ result = DriverMatrixService().tested_versions_report(build_id=build_id)
21
+ return {
22
+ "status": "ok",
23
+ "response": result
24
+ }
@@ -0,0 +1,175 @@
1
+ from dataclasses import dataclass
2
+ from datetime import datetime
3
+ from functools import reduce
4
+ import re
5
+ from uuid import UUID
6
+ from cassandra.cqlengine import columns
7
+ from argus.backend.db import ScyllaCluster
8
+ from argus.backend.models.web import ArgusRelease
9
+ from argus.backend.plugins.core import PluginModelBase
10
+ from argus.backend.plugins.driver_matrix_tests.udt import TestCollection, TestSuite, TestCase, EnvironmentInfo
11
+ from argus.backend.plugins.driver_matrix_tests.raw_types import RawMatrixTestResult
12
+ from argus.backend.util.enums import TestStatus
13
+
14
+
15
+ class DriverMatrixPluginError(Exception):
16
+ pass
17
+
18
+
19
+ @dataclass(init=True, repr=True, frozen=True)
20
+ class DriverMatrixRunSubmissionRequest():
21
+ schema_version: str
22
+ run_id: str
23
+ job_name: str
24
+ job_url: str
25
+ test_environment: dict[str, str]
26
+ matrix_results: list[RawMatrixTestResult]
27
+
28
+
29
+ class DriverTestRun(PluginModelBase):
30
+ _plugin_name = "driver-matrix-tests"
31
+ __table_name__ = "driver_test_run"
32
+ scylla_version = columns.Text()
33
+ test_collection = columns.List(value_type=columns.UserDefinedType(user_type=TestCollection))
34
+ environment_info = columns.List(value_type=columns.UserDefinedType(user_type=EnvironmentInfo))
35
+
36
+ _no_upstream = ["rust"]
37
+
38
+ _artifact_fnames = {
39
+ "cpp": r"TEST-(?P<driver_name>[\w]*)-(?P<version>[\d\.-]*)",
40
+ "gocql": r"xunit\.(?P<driver_name>[\w]*)\.(?P<proto>v\d)\.(?P<version>[v\d\.]*)",
41
+ "python": r"pytest\.(?P<driver_name>[\w]*)\.(?P<proto>v\d)\.(?P<version>[\d\.]*)",
42
+ "java": r"TEST-(?P<version>[\d\.\w-]*)",
43
+ "rust": r"(?P<driver_name>rust)_results_v(?P<version>[\d\w\-.]*)",
44
+ }
45
+
46
+ @classmethod
47
+ def _stats_query(cls) -> str:
48
+ return ("SELECT id, test_id, group_id, release_id, status, start_time, build_job_url, build_id, "
49
+ f"assignee, end_time, investigation_status, heartbeat, scylla_version FROM {cls.table_name()} WHERE build_id IN ? PER PARTITION LIMIT 15")
50
+
51
+ @classmethod
52
+ def get_distinct_product_versions(cls, release: ArgusRelease) -> list[str]:
53
+ cluster = ScyllaCluster.get()
54
+ statement = cluster.prepare(f"SELECT scylla_version FROM {cls.table_name()} WHERE release_id = ?")
55
+ rows = cluster.session.execute(query=statement, parameters=(release.id,))
56
+ unique_versions = {r["scylla_version"] for r in rows if r["scylla_version"]}
57
+
58
+ return sorted(list(unique_versions), reverse=True)
59
+
60
+ @classmethod
61
+ def load_test_run(cls, run_id: UUID) -> 'DriverTestRun':
62
+ return cls.get(id=run_id)
63
+
64
+ @classmethod
65
+ def parse_driver_name(cls, raw_file_name: str) -> str:
66
+ for test, pattern in cls._artifact_fnames.items():
67
+ match = re.match(pattern, raw_file_name)
68
+ if not match:
69
+ continue
70
+ driver_info = match.groupdict()
71
+ if test == "java":
72
+ version = driver_info["version"]
73
+ return "scylla" if len(version.split(".")) > 3 or "scylla" in version else "datastax"
74
+ else:
75
+ return driver_info["driver_name"]
76
+ return "unknown_driver"
77
+
78
+
79
+ @classmethod
80
+ def submit_run(cls, request_data: dict) -> 'DriverTestRun':
81
+ req = DriverMatrixRunSubmissionRequest(**request_data)
82
+ run = cls()
83
+ run.id = req.run_id # pylint: disable=invalid-name
84
+ run.build_id = req.job_name
85
+ run.build_job_url = req.job_url
86
+ run.assign_categories()
87
+ try:
88
+ run.assignee = run.get_scheduled_assignee()
89
+ except Exception: # pylint: disable=broad-except
90
+ run.assignee = None
91
+ for key, value in req.test_environment.items():
92
+ env_info = EnvironmentInfo()
93
+ env_info.key = key
94
+ env_info.value = value
95
+ run.environment_info.append(env_info)
96
+
97
+ run.scylla_version = req.test_environment.get("scylla-version")
98
+ run.test_collection = []
99
+
100
+ for result in req.matrix_results:
101
+ collection = TestCollection()
102
+ collection.name = result.get("name")
103
+ collection.driver = cls.parse_driver_name(collection.name)
104
+ collection.tests_total = result.get("tests")
105
+ collection.failures = result.get("failures")
106
+ collection.errors = result.get("errors")
107
+ collection.skipped = result.get("skipped")
108
+ collection.passed = result.get("passed")
109
+ collection.disabled = result.get("disabled")
110
+ collection.time = result.get("time")
111
+ timestamp = result.get("timestamp")
112
+ # TODO: Not needed once python>=3.11
113
+ timestamp = timestamp[0:-1] if timestamp[-1] == "Z" else timestamp
114
+ collection.timestamp = datetime.fromisoformat(timestamp)
115
+ for raw_suite in result.get("suites"):
116
+ suite = TestSuite()
117
+ suite.name = raw_suite.get("name")
118
+ suite.tests_total = raw_suite.get("tests")
119
+ suite.failures = raw_suite.get("failures")
120
+ suite.errors = raw_suite.get("errors")
121
+ suite.skipped = raw_suite.get("skipped")
122
+ suite.passed = raw_suite.get("passed")
123
+ suite.disabled = raw_suite.get("disabled")
124
+ suite.time = raw_suite.get("time")
125
+
126
+ for raw_case in raw_suite.get("cases"):
127
+ case = TestCase()
128
+ case.name = raw_case.get("name")
129
+ case.status = raw_case.get("status")
130
+ case.time = raw_case.get("time")
131
+ case.classname = raw_case.get("classname")
132
+ case.message = raw_case.get("message")
133
+ suite.cases.append(case)
134
+
135
+ collection.suites.append(suite)
136
+ run.test_collection.append(collection)
137
+
138
+ run.status = run._determine_run_status().value
139
+ run.save()
140
+ return run
141
+
142
+ def get_resources(self) -> list:
143
+ return []
144
+
145
+ def get_nemeses(self) -> list:
146
+ return []
147
+
148
+ def _determine_run_status(self):
149
+ if len(self.test_collection) < 2:
150
+ return TestStatus.FAILED
151
+
152
+ driver_types = {collection.driver for collection in self.test_collection}
153
+ if len(driver_types) <= 1 and not any(driver for driver in self._no_upstream if driver in driver_types):
154
+ return TestStatus.FAILED
155
+
156
+ failure_count = reduce(lambda acc, val: acc + (val.failures + val.errors), self.test_collection, 0)
157
+ if failure_count > 0:
158
+ return TestStatus.FAILED
159
+
160
+ return TestStatus.PASSED
161
+
162
+ def change_status(self, new_status: TestStatus):
163
+ raise DriverMatrixPluginError("This method is obsolete. Status is now determined on submission.")
164
+
165
+ def get_events(self) -> list:
166
+ return []
167
+
168
+ def submit_product_version(self, version: str):
169
+ self.scylla_version = version
170
+
171
+ def finish_run(self, payload: dict = None):
172
+ self.end_time = datetime.utcnow()
173
+
174
+ def submit_logs(self, logs: list[dict]):
175
+ pass
@@ -1,6 +1,4 @@
1
- from dataclasses import dataclass
2
1
  from typing import TypedDict
3
- from uuid import UUID
4
2
 
5
3
 
6
4
  class RawMatrixTestCase(TypedDict):
@@ -35,28 +33,3 @@ class RawMatrixTestResult(TypedDict):
35
33
  time: float
36
34
  timestamp: str
37
35
  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,13 +1,8 @@
1
- import base64
2
- import logging
3
- from uuid import UUID
4
1
  from argus.backend.db import ScyllaCluster
5
2
  from argus.backend.models.web import ArgusRelease, ArgusTest
6
3
  from argus.backend.plugins.driver_matrix_tests.model import DriverTestRun
7
4
 
8
5
 
9
- LOGGER = logging.getLogger(__name__)
10
-
11
6
  class DriverMatrixService:
12
7
  def tested_versions_report(self, build_id: str) -> dict:
13
8
  db = ScyllaCluster.get()
@@ -45,16 +40,3 @@ class DriverMatrixService:
45
40
  "versions": version_map,
46
41
  }
47
42
  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(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)
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()
21
21
  time = columns.Float()
22
22
  cases = columns.List(value_type=columns.UserDefinedType(user_type=TestCase))
23
23
 
@@ -25,15 +25,14 @@ class TestSuite(UserType):
25
25
  class TestCollection(UserType):
26
26
  name = columns.Text()
27
27
  driver = columns.Text()
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)
28
+ tests_total = columns.Integer()
29
+ failures = columns.Integer()
30
+ disabled = columns.Integer()
31
+ skipped = columns.Integer()
32
+ passed = columns.Integer()
33
+ errors = columns.Integer()
35
34
  timestamp = columns.DateTime()
36
- time = columns.Float(default=lambda: 0.0)
35
+ time = columns.Float()
37
36
  suites = columns.List(value_type=columns.UserDefinedType(user_type=TestSuite))
38
37
 
39
38
 
@@ -249,6 +249,13 @@ class SCTTestRun(PluginModelBase):
249
249
 
250
250
  self._collect_event_message(event, event_message)
251
251
 
252
+ def sut_timestamp(self) -> float:
253
+ """converts scylla-server date to timestamp and adds revision in subseconds precision to diffirentiate
254
+ scylla versions from the same day. It's not perfect, but we don't know exact version time."""
255
+ scylla_package = [package for package in self.packages if package.name == "scylla-server"][0]
256
+ return (datetime.strptime(scylla_package.date, '%Y%m%d').timestamp()
257
+ + int(scylla_package.revision_id, 16) % 1000000 / 1000000)
258
+
252
259
 
253
260
  class SCTJunitReports(Model):
254
261
  test_id = columns.UUID(primary_key=True, partition_key=True, required=True)
@@ -1,5 +1,6 @@
1
1
  from uuid import UUID
2
2
  from argus.backend.db import ScyllaCluster
3
+ from argus.backend.models.result import ArgusGenericResultMetadata, ArgusGenericResultData
3
4
  from argus.backend.plugins.core import PluginModelBase
4
5
  from argus.backend.plugins.loader import AVAILABLE_PLUGINS
5
6
  from argus.backend.util.enums import TestStatus
@@ -69,3 +70,20 @@ class ClientService:
69
70
  run.save()
70
71
 
71
72
  return "Finalized"
73
+
74
+ def submit_results(self, run_type: str, run_id: str, results: dict) -> str:
75
+ model = self.get_model(run_type)
76
+ run = model.load_test_run(UUID(run_id))
77
+ ArgusGenericResultMetadata(test_id=run.test_id, **results["meta"]).save()
78
+ if results.get("sut_timestamp", 0) == 0:
79
+ results["sut_timestamp"] = run.sut_timestamp() # automatic sut_timestamp
80
+ table_name = results["meta"]["name"]
81
+ sut_timestamp = results["sut_timestamp"]
82
+ for cell in results["results"]:
83
+ ArgusGenericResultData(test_id=run.test_id,
84
+ run_id=run.id,
85
+ name=table_name,
86
+ sut_timestamp=sut_timestamp,
87
+ **cell
88
+ ).save()
89
+ return "Submitted"
@@ -13,6 +13,7 @@ from flask import g
13
13
  from cassandra.query import BatchStatement, ConsistencyLevel
14
14
  from cassandra.cqlengine.query import BatchQuery
15
15
  from argus.backend.db import ScyllaCluster
16
+ from argus.backend.models.result import ArgusGenericResultMetadata, ArgusGenericResultData
16
17
 
17
18
  from argus.backend.models.web import (
18
19
  ArgusEvent,
@@ -306,6 +307,24 @@ class TestRunService:
306
307
  }
307
308
  return response
308
309
 
310
+ def fetch_results(self, test_id: UUID, run_id: UUID) -> dict:
311
+ query_fields = ["column", "row", "value", "status"]
312
+ tables_meta = ArgusGenericResultMetadata.filter(test_id=test_id)
313
+ tables = []
314
+ for table in tables_meta:
315
+ cells = ArgusGenericResultData.objects.filter(test_id=test_id, run_id=run_id, name=table.name).only(query_fields)
316
+ if not cells:
317
+ continue
318
+ tables.append({'meta': {
319
+ 'name': table.name,
320
+ 'description': table.description,
321
+ 'columns_meta': table.columns_meta,
322
+ 'rows_meta': table.rows_meta
323
+ },
324
+ 'cells': [{k:v for k,v in cell.items() if k in query_fields} for cell in cells]})
325
+
326
+ return tables
327
+
309
328
  def submit_github_issue(self, issue_url: str, test_id: UUID, run_id: UUID):
310
329
  user_tokens = UserOauthToken.filter(user_id=g.user.id).all()
311
330
  token = None
@@ -7,6 +7,7 @@ from uuid import UUID
7
7
  import requests
8
8
 
9
9
  from argus.backend.util.enums import TestStatus
10
+ from argus.client.generic_result import GenericResultTable
10
11
  from argus.client.sct.types import LogLink
11
12
 
12
13
  JSON = dict[str, Any] | list[Any] | int | str | float | bool | Type[None]
@@ -28,6 +29,8 @@ class ArgusClient:
28
29
  SET_STATUS = "/testrun/$type/$id/set_status"
29
30
  SET_PRODUCT_VERSION = "/testrun/$type/$id/update_product_version"
30
31
  SUBMIT_LOGS = "/testrun/$type/$id/logs/submit"
32
+ SUBMIT_RESULTS = "/testrun/$type/$id/submit_results"
33
+ FETCH_RESULTS = "/testrun/$type/$id/fetch_results"
31
34
  FINALIZE = "/testrun/$type/$id/finalize"
32
35
 
33
36
  def __init__(self, auth_token: str, base_url: str, api_version="v1") -> None:
@@ -190,3 +193,16 @@ class ArgusClient:
190
193
  }
191
194
  )
192
195
  self.check_response(response)
196
+
197
+ def submit_results(self, result: GenericResultTable) -> None:
198
+ response = self.post(
199
+ endpoint=self.Routes.SUBMIT_RESULTS,
200
+ location_params={"type": self.test_type, "id": str(self.run_id)},
201
+ body={
202
+ **self.generic_body,
203
+ "run_id": str(self.run_id),
204
+ ** result.as_dict(),
205
+ }
206
+ )
207
+ self.check_response(response)
208
+