argus-alm 0.11.3__tar.gz → 0.11.6__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.
- {argus_alm-0.11.3 → argus_alm-0.11.6}/PKG-INFO +1 -1
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/controller/api.py +12 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/controller/main.py +1 -3
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/controller/team.py +1 -1
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/controller/testrun_api.py +16 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/events/event_processors.py +1 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/models/web.py +1 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/plugins/core.py +17 -1
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/plugins/driver_matrix_tests/model.py +5 -1
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/plugins/sct/controller.py +47 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/plugins/sct/plugin.py +3 -1
- argus_alm-0.11.6/argus/backend/plugins/sct/service.py +393 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/plugins/sct/testrun.py +78 -21
- argus_alm-0.11.6/argus/backend/plugins/sct/types.py +38 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/plugins/sct/udt.py +13 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/service/argus_service.py +16 -32
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/service/build_system_monitor.py +21 -3
- argus_alm-0.11.6/argus/backend/service/event_service.py +18 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/service/stats.py +69 -15
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/service/testrun.py +59 -21
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/util/encoders.py +3 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/util/enums.py +1 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/client/driver_matrix_tests/client.py +19 -6
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/client/sct/client.py +50 -4
- {argus_alm-0.11.3 → argus_alm-0.11.6}/pyproject.toml +1 -1
- argus_alm-0.11.3/argus/backend/plugins/sct/service.py +0 -219
- argus_alm-0.11.3/setup.py +0 -45
- {argus_alm-0.11.3 → argus_alm-0.11.6}/LICENSE +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/README.md +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/__init__.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/.gitkeep +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/__init__.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/cli.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/controller/__init__.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/controller/admin.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/controller/admin_api.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/controller/auth.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/controller/client_api.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/controller/notification_api.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/controller/notifications.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/controller/team_ui.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/db.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/error_handlers.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/models/__init__.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/plugins/__init__.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/plugins/driver_matrix_tests/controller.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/plugins/driver_matrix_tests/plugin.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/plugins/driver_matrix_tests/raw_types.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/plugins/driver_matrix_tests/udt.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/plugins/loader.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/plugins/sct/resource_setup.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/plugins/sirenada/model.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/plugins/sirenada/plugin.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/plugins/sirenada/types.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/service/admin.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/service/client_service.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/service/notification_manager.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/service/release_manager.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/service/team_manager_service.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/service/user.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/template_filters.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/util/common.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/util/config.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/util/logsetup.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/util/module_loaders.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/backend/util/send_email.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/client/__init__.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/client/base.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/client/sct/types.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/client/sirenada/client.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/db/.gitkeep +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/db/argus_json.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/db/cloud_types.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/db/config.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/db/db_types.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/db/interface.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/db/testrun.py +0 -0
- {argus_alm-0.11.3 → argus_alm-0.11.6}/argus/db/utils.py +0 -0
|
@@ -472,3 +472,15 @@ def resolve_artifact_size():
|
|
|
472
472
|
"artifactSize": length,
|
|
473
473
|
}
|
|
474
474
|
}
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
@bp.route("/user/jobs")
|
|
478
|
+
@api_login_required
|
|
479
|
+
def user_jobs():
|
|
480
|
+
service = ArgusService()
|
|
481
|
+
result = list(service.get_jobs_for_user(user=g.user))
|
|
482
|
+
|
|
483
|
+
return {
|
|
484
|
+
"status": "ok",
|
|
485
|
+
"response": result
|
|
486
|
+
}
|
|
@@ -241,9 +241,7 @@ def update_password():
|
|
|
241
241
|
@bp.route("/profile/jobs", methods=["GET"])
|
|
242
242
|
@login_required
|
|
243
243
|
def profile_jobs():
|
|
244
|
-
|
|
245
|
-
jobs = service.get_jobs_for_user(g.user)
|
|
246
|
-
return render_template("profile_jobs.html.j2", runs=jobs)
|
|
244
|
+
return render_template("profile_jobs.html.j2")
|
|
247
245
|
|
|
248
246
|
|
|
249
247
|
@bp.route("/profile/schedules", methods=["GET"])
|
|
@@ -107,7 +107,7 @@ def user_teams(user_id: str):
|
|
|
107
107
|
@api_login_required
|
|
108
108
|
def user_jobs(user_id: str):
|
|
109
109
|
user = User.get(id=UUID(user_id))
|
|
110
|
-
result = ArgusService().get_jobs_for_user(user)
|
|
110
|
+
result = list(ArgusService().get_jobs_for_user(user))
|
|
111
111
|
|
|
112
112
|
return {
|
|
113
113
|
"status": "ok",
|
|
@@ -269,3 +269,19 @@ def sct_terminate_stuck_runs():
|
|
|
269
269
|
"total": result
|
|
270
270
|
}
|
|
271
271
|
}
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
@bp.route("/ignore_jobs", methods=["POST"])
|
|
275
|
+
@api_login_required
|
|
276
|
+
def ignore_jobs():
|
|
277
|
+
payload = get_payload(request)
|
|
278
|
+
service = TestRunService()
|
|
279
|
+
|
|
280
|
+
result = service.ignore_jobs(test_id=payload["testId"], reason=payload["reason"])
|
|
281
|
+
|
|
282
|
+
return {
|
|
283
|
+
"status": "ok",
|
|
284
|
+
"response": {
|
|
285
|
+
"affectedJobs": result
|
|
286
|
+
}
|
|
287
|
+
}
|
|
@@ -30,4 +30,5 @@ EVENT_PROCESSORS = {
|
|
|
30
30
|
ArgusEventTypes.TestRunIssueAdded: event_process_issue_added,
|
|
31
31
|
ArgusEventTypes.TestRunIssueRemoved: event_process_issue_added,
|
|
32
32
|
ArgusEventTypes.TestRunInvestigationStatusChanged: event_process_investigation_status_changed,
|
|
33
|
+
ArgusEventTypes.TestRunBatchInvestigationStatusChange: event_process_investigation_status_changed,
|
|
33
34
|
}
|
|
@@ -190,6 +190,7 @@ class ArgusEventTypes(str, Enum):
|
|
|
190
190
|
AssigneeChanged = "ARGUS_ASSIGNEE_CHANGE"
|
|
191
191
|
TestRunStatusChanged = "ARGUS_TEST_RUN_STATUS_CHANGE"
|
|
192
192
|
TestRunInvestigationStatusChanged = "ARGUS_TEST_RUN_INVESTIGATION_STATUS_CHANGE"
|
|
193
|
+
TestRunBatchInvestigationStatusChange = "ARGUS_TEST_RUN_INVESTIGATION_BATCH_STATUS_CHANGE"
|
|
193
194
|
TestRunCommentPosted = "ARGUS_TEST_RUN_COMMENT_POSTED"
|
|
194
195
|
TestRunCommentUpdated = "ARGUS_TEST_RUN_COMMENT_UPDATED"
|
|
195
196
|
TestRunCommentDeleted = "ARGUS_TEST_RUN_COMMENT_DELETED"
|
|
@@ -109,11 +109,27 @@ class PluginModelBase(Model):
|
|
|
109
109
|
def get_jobs_assigned_to_user(cls, user: User):
|
|
110
110
|
cluster = ScyllaCluster.get()
|
|
111
111
|
query = cluster.prepare("SELECT build_id, start_time, release_id, group_id, assignee, "
|
|
112
|
-
f"test_id, id, status, investigation_status, build_job_url FROM {cls.table_name()} WHERE assignee = ?")
|
|
112
|
+
f"test_id, id, status, investigation_status, build_job_url, scylla_version FROM {cls.table_name()} WHERE assignee = ?")
|
|
113
113
|
rows = cluster.session.execute(query=query, parameters=(user.id,))
|
|
114
114
|
|
|
115
115
|
return list(rows)
|
|
116
116
|
|
|
117
|
+
@classmethod
|
|
118
|
+
def get_jobs_meta_by_test_id(cls, test_id: UUID):
|
|
119
|
+
cluster = ScyllaCluster.get()
|
|
120
|
+
query = cluster.prepare(f"SELECT build_id, start_time, id, test_id, release_id, group_id, status, investigation_status FROM {cls.table_name()} WHERE test_id = ?")
|
|
121
|
+
rows = cluster.session.execute(query=query, parameters=(test_id,))
|
|
122
|
+
|
|
123
|
+
return list(rows)
|
|
124
|
+
|
|
125
|
+
@classmethod
|
|
126
|
+
def prepare_investigation_status_update_query(cls, build_id: str, start_time: datetime, new_status: TestInvestigationStatus):
|
|
127
|
+
cluster = ScyllaCluster.get()
|
|
128
|
+
query = cluster.prepare(f"UPDATE {cls.table_name()} SET investigation_status = ? WHERE build_id = ? AND start_time = ?")
|
|
129
|
+
bound_query = query.bind(values=(new_status.value, build_id, start_time))
|
|
130
|
+
|
|
131
|
+
return bound_query
|
|
132
|
+
|
|
117
133
|
@classmethod
|
|
118
134
|
def get_stats_for_release(cls, release: ArgusRelease):
|
|
119
135
|
cluster = ScyllaCluster.get()
|
|
@@ -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
|
|
|
@@ -31,6 +31,23 @@ def sct_submit_screenshots(run_id: str):
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
@bp.route("/<string:run_id>/sct_runner/set", methods=["POST"])
|
|
35
|
+
@api_login_required
|
|
36
|
+
def sct_set_runner(run_id: str):
|
|
37
|
+
payload = get_payload(request)
|
|
38
|
+
result = SCTService.set_sct_runner(
|
|
39
|
+
run_id=run_id,
|
|
40
|
+
public_ip=payload["public_ip"],
|
|
41
|
+
private_ip=payload["private_ip"],
|
|
42
|
+
region=payload["region"],
|
|
43
|
+
backend=payload["backend"]
|
|
44
|
+
)
|
|
45
|
+
return {
|
|
46
|
+
"status": "ok",
|
|
47
|
+
"response": result
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
34
51
|
@bp.route("/<string:run_id>/resource/create", methods=["POST"])
|
|
35
52
|
@api_login_required
|
|
36
53
|
def sct_resource_create(run_id: str):
|
|
@@ -97,6 +114,36 @@ def sct_events_submit(run_id: str):
|
|
|
97
114
|
}
|
|
98
115
|
|
|
99
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
|
+
|
|
100
147
|
@bp.route("/release/<string:release_name>/kernels", methods=["GET"])
|
|
101
148
|
@api_login_required
|
|
102
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
|
]
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from functools import reduce
|
|
3
|
+
import logging
|
|
4
|
+
import math
|
|
5
|
+
from time import time
|
|
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
|
|
10
|
+
from argus.backend.plugins.sct.udt import (
|
|
11
|
+
CloudInstanceDetails,
|
|
12
|
+
CloudResource,
|
|
13
|
+
EventsBySeverity,
|
|
14
|
+
NemesisRunInfo,
|
|
15
|
+
NodeDescription,
|
|
16
|
+
PackageVersion,
|
|
17
|
+
PerformanceHDRHistogram,
|
|
18
|
+
)
|
|
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
|
|
22
|
+
|
|
23
|
+
LOGGER = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class SCTServiceException(Exception):
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(init=True, repr=True)
|
|
31
|
+
class NemesisSubmissionRequest:
|
|
32
|
+
name: str
|
|
33
|
+
class_name: str
|
|
34
|
+
start_time: int
|
|
35
|
+
node_name: str
|
|
36
|
+
node_ip: str
|
|
37
|
+
node_shards: int
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(init=True, repr=True)
|
|
41
|
+
class NemesisFinalizationRequest:
|
|
42
|
+
name: str
|
|
43
|
+
start_time: int
|
|
44
|
+
status: str
|
|
45
|
+
message: str
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(init=True, repr=True)
|
|
49
|
+
class EventSubmissionRequest:
|
|
50
|
+
severity: str
|
|
51
|
+
total_events: int
|
|
52
|
+
messages: list[str]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class SCTService:
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
def submit_packages(run_id: str, packages: list[dict]) -> str:
|
|
59
|
+
try:
|
|
60
|
+
run: SCTTestRun = SCTTestRun.get(id=run_id)
|
|
61
|
+
for package_dict in packages:
|
|
62
|
+
package = PackageVersion(**package_dict)
|
|
63
|
+
run.packages.append(package)
|
|
64
|
+
run.save()
|
|
65
|
+
except SCTTestRun.DoesNotExist as exception:
|
|
66
|
+
LOGGER.error("Run %s not found for SCTTestRun", run_id)
|
|
67
|
+
raise SCTServiceException("Run not found", run_id) from exception
|
|
68
|
+
|
|
69
|
+
return "added"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def set_sct_runner(run_id: str, public_ip: str, private_ip: str, region: str, backend: str):
|
|
74
|
+
try:
|
|
75
|
+
run: SCTTestRun = SCTTestRun.get(id=run_id)
|
|
76
|
+
run.sct_runner_host = CloudInstanceDetails(
|
|
77
|
+
public_ip=public_ip,
|
|
78
|
+
private_ip=private_ip,
|
|
79
|
+
provider=backend,
|
|
80
|
+
region=region,
|
|
81
|
+
)
|
|
82
|
+
run.save()
|
|
83
|
+
except SCTTestRun.DoesNotExist as exception:
|
|
84
|
+
LOGGER.error("Run %s not found for SCTTestRun", run_id)
|
|
85
|
+
raise SCTServiceException("Run not found", run_id) from exception
|
|
86
|
+
|
|
87
|
+
return "updated"
|
|
88
|
+
|
|
89
|
+
@staticmethod
|
|
90
|
+
def submit_screenshots(run_id: str, screenshot_links: list[str]) -> str:
|
|
91
|
+
try:
|
|
92
|
+
run: SCTTestRun = SCTTestRun.get(id=run_id)
|
|
93
|
+
for link in screenshot_links:
|
|
94
|
+
run.add_screenshot(link)
|
|
95
|
+
run.save()
|
|
96
|
+
except SCTTestRun.DoesNotExist as exception:
|
|
97
|
+
LOGGER.error("Run %s not found for SCTTestRun", run_id)
|
|
98
|
+
raise SCTServiceException("Run not found", run_id) from exception
|
|
99
|
+
|
|
100
|
+
return "submitted"
|
|
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
|
+
|
|
250
|
+
@staticmethod
|
|
251
|
+
def create_resource(run_id: str, resource_details: dict) -> str:
|
|
252
|
+
instance_details = CloudInstanceDetails(**resource_details.pop("instance_details"))
|
|
253
|
+
resource = CloudResource(**resource_details, instance_info=instance_details)
|
|
254
|
+
try:
|
|
255
|
+
run: SCTTestRun = SCTTestRun.get(id=run_id)
|
|
256
|
+
run.get_resources().append(resource)
|
|
257
|
+
run.save()
|
|
258
|
+
except SCTTestRun.DoesNotExist as exception:
|
|
259
|
+
LOGGER.error("Run %s not found for SCTTestRun", run_id)
|
|
260
|
+
raise SCTServiceException("Run not found", run_id) from exception
|
|
261
|
+
|
|
262
|
+
return "created"
|
|
263
|
+
|
|
264
|
+
@staticmethod
|
|
265
|
+
def update_resource_shards(run_id: str, resource_name: str, new_shards: int) -> str:
|
|
266
|
+
try:
|
|
267
|
+
run: SCTTestRun = SCTTestRun.get(id=run_id)
|
|
268
|
+
resource = next(res for res in run.get_resources() if res.name == resource_name)
|
|
269
|
+
resource.get_instance_info().shards_amount = new_shards
|
|
270
|
+
run.save()
|
|
271
|
+
except StopIteration as exception:
|
|
272
|
+
LOGGER.error("Resource %s not found in run %s", resource_name, run_id)
|
|
273
|
+
raise SCTServiceException("Resource not found", resource_name) from exception
|
|
274
|
+
except SCTTestRun.DoesNotExist as exception:
|
|
275
|
+
LOGGER.error("Run %s not found for SCTTestRun", run_id)
|
|
276
|
+
raise SCTServiceException("Run not found", run_id) from exception
|
|
277
|
+
|
|
278
|
+
return "updated"
|
|
279
|
+
|
|
280
|
+
@staticmethod
|
|
281
|
+
def terminate_resource(run_id: str, resource_name: str, reason: str) -> str:
|
|
282
|
+
try:
|
|
283
|
+
run: SCTTestRun = SCTTestRun.get(id=run_id)
|
|
284
|
+
resource = next(res for res in run.get_resources() if res.name == resource_name)
|
|
285
|
+
resource.get_instance_info().termination_reason = reason
|
|
286
|
+
resource.get_instance_info().termination_time = int(time())
|
|
287
|
+
resource.state = ResourceState.TERMINATED.value
|
|
288
|
+
run.save()
|
|
289
|
+
except StopIteration as exception:
|
|
290
|
+
LOGGER.error("Resource %s not found in run %s", resource_name, run_id)
|
|
291
|
+
raise SCTServiceException("Resource not found", resource_name) from exception
|
|
292
|
+
except SCTTestRun.DoesNotExist as exception:
|
|
293
|
+
LOGGER.error("Run %s not found for SCTTestRun", run_id)
|
|
294
|
+
raise SCTServiceException("Run not found", run_id) from exception
|
|
295
|
+
|
|
296
|
+
return "terminated"
|
|
297
|
+
|
|
298
|
+
@staticmethod
|
|
299
|
+
def submit_nemesis(run_id: str, nemesis_details: dict) -> str:
|
|
300
|
+
nem_req = NemesisSubmissionRequest(**nemesis_details)
|
|
301
|
+
node_desc = NodeDescription(name=nem_req.node_name, ip=nem_req.node_ip, shards=nem_req.node_shards)
|
|
302
|
+
nemesis_info = NemesisRunInfo(
|
|
303
|
+
class_name=nem_req.class_name,
|
|
304
|
+
name=nem_req.name,
|
|
305
|
+
start_time=int(nem_req.start_time),
|
|
306
|
+
end_time=0,
|
|
307
|
+
duration=0,
|
|
308
|
+
stack_trace="",
|
|
309
|
+
status=NemesisStatus.RUNNING.value,
|
|
310
|
+
target_node=node_desc,
|
|
311
|
+
)
|
|
312
|
+
try:
|
|
313
|
+
run: SCTTestRun = SCTTestRun.get(id=run_id)
|
|
314
|
+
run.add_nemesis(nemesis_info)
|
|
315
|
+
run.save()
|
|
316
|
+
except SCTTestRun.DoesNotExist as exception:
|
|
317
|
+
LOGGER.error("Run %s not found for SCTTestRun", run_id)
|
|
318
|
+
raise SCTServiceException("Run not found", run_id) from exception
|
|
319
|
+
|
|
320
|
+
return "created"
|
|
321
|
+
|
|
322
|
+
@staticmethod
|
|
323
|
+
def finalize_nemesis(run_id: str, nemesis_details: dict) -> str:
|
|
324
|
+
nem_req = NemesisFinalizationRequest(**nemesis_details)
|
|
325
|
+
try:
|
|
326
|
+
run: SCTTestRun = SCTTestRun.get(id=run_id)
|
|
327
|
+
nemesis = next(nem for nem in run.get_nemeses() if nem.name ==
|
|
328
|
+
nem_req.name and nem.start_time == nem_req.start_time)
|
|
329
|
+
nemesis.status = NemesisStatus(nem_req.status).value
|
|
330
|
+
nemesis.stack_trace = nem_req.message
|
|
331
|
+
nemesis.end_time = int(time())
|
|
332
|
+
run.save()
|
|
333
|
+
except StopIteration as exception:
|
|
334
|
+
LOGGER.error("Nemesis %s (%s) not found for run %s", nem_req.name, nem_req.start_time, run_id)
|
|
335
|
+
raise SCTServiceException("Nemesis not found", (nem_req.name, nem_req.start_time)) from exception
|
|
336
|
+
except SCTTestRun.DoesNotExist as exception:
|
|
337
|
+
LOGGER.error("Run %s not found for SCTTestRun", run_id)
|
|
338
|
+
raise SCTServiceException("Run not found", run_id) from exception
|
|
339
|
+
|
|
340
|
+
return "updated"
|
|
341
|
+
|
|
342
|
+
@staticmethod
|
|
343
|
+
def submit_events(run_id: str, events: list[dict]) -> str:
|
|
344
|
+
wrapped_events = [EventSubmissionRequest(**ev) for ev in events]
|
|
345
|
+
try:
|
|
346
|
+
run: SCTTestRun = SCTTestRun.get(id=run_id)
|
|
347
|
+
for event in wrapped_events:
|
|
348
|
+
wrapper = EventsBySeverity(severity=event.severity,
|
|
349
|
+
event_amount=event.total_events, last_events=event.messages)
|
|
350
|
+
run.get_events().append(wrapper)
|
|
351
|
+
run.save()
|
|
352
|
+
except SCTTestRun.DoesNotExist as exception:
|
|
353
|
+
LOGGER.error("Run %s not found for SCTTestRun", run_id)
|
|
354
|
+
raise SCTServiceException("Run not found", run_id) from exception
|
|
355
|
+
|
|
356
|
+
return "added"
|
|
357
|
+
|
|
358
|
+
@staticmethod
|
|
359
|
+
def get_scylla_version_kernels_report(release_name: str):
|
|
360
|
+
all_release_runs = SCTTestRun.get_version_data_for_release(release_name=release_name)
|
|
361
|
+
kernels_by_version = {}
|
|
362
|
+
kernel_metadata = {}
|
|
363
|
+
for run in all_release_runs:
|
|
364
|
+
packages = run["packages"]
|
|
365
|
+
if not packages:
|
|
366
|
+
continue
|
|
367
|
+
scylla_pkgs = {p["name"]: p for p in packages if "scylla-server" in p["name"]}
|
|
368
|
+
scylla_pkg = scylla_pkgs["scylla-server-upgraded"] if scylla_pkgs.get(
|
|
369
|
+
"scylla-server-upgraded") else scylla_pkgs.get("scylla-server")
|
|
370
|
+
version = f"{scylla_pkg['version']}-{scylla_pkg['date']}.{scylla_pkg['revision_id']}" if scylla_pkgs else "unknown"
|
|
371
|
+
kernel_packages = [p for p in packages if "kernel" in p["name"]]
|
|
372
|
+
kernel_package = kernel_packages[0] if len(kernel_packages) > 0 else None
|
|
373
|
+
if not kernel_package:
|
|
374
|
+
continue
|
|
375
|
+
version_list = set(kernels_by_version.get(version, []))
|
|
376
|
+
version_list.add(kernel_package["version"])
|
|
377
|
+
kernels_by_version[version] = list(version_list)
|
|
378
|
+
metadata = kernel_metadata.get(
|
|
379
|
+
kernel_package.version,
|
|
380
|
+
{
|
|
381
|
+
"passed": 0,
|
|
382
|
+
"failed": 0,
|
|
383
|
+
"aborted": 0,
|
|
384
|
+
}
|
|
385
|
+
)
|
|
386
|
+
if run["status"] in ["passed", "failed", "aborted"]:
|
|
387
|
+
metadata[run["status"]] += 1
|
|
388
|
+
kernel_metadata[kernel_package["version"]] = metadata
|
|
389
|
+
|
|
390
|
+
return {
|
|
391
|
+
"versions": kernels_by_version,
|
|
392
|
+
"metadata": kernel_metadata
|
|
393
|
+
}
|