argus-alm 0.14.2__py3-none-any.whl → 0.15.2__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/_version.py +21 -0
- argus/backend/.gitkeep +0 -0
- argus/backend/__init__.py +0 -0
- argus/backend/cli.py +57 -0
- argus/backend/controller/__init__.py +0 -0
- argus/backend/controller/admin.py +20 -0
- argus/backend/controller/admin_api.py +355 -0
- argus/backend/controller/api.py +589 -0
- argus/backend/controller/auth.py +67 -0
- argus/backend/controller/client_api.py +109 -0
- argus/backend/controller/main.py +316 -0
- argus/backend/controller/notification_api.py +72 -0
- argus/backend/controller/notifications.py +13 -0
- argus/backend/controller/planner_api.py +194 -0
- argus/backend/controller/team.py +129 -0
- argus/backend/controller/team_ui.py +19 -0
- argus/backend/controller/testrun_api.py +513 -0
- argus/backend/controller/view_api.py +188 -0
- argus/backend/controller/views_widgets/__init__.py +0 -0
- argus/backend/controller/views_widgets/graphed_stats.py +54 -0
- argus/backend/controller/views_widgets/graphs.py +68 -0
- argus/backend/controller/views_widgets/highlights.py +135 -0
- argus/backend/controller/views_widgets/nemesis_stats.py +26 -0
- argus/backend/controller/views_widgets/summary.py +43 -0
- argus/backend/db.py +98 -0
- argus/backend/error_handlers.py +41 -0
- argus/backend/events/event_processors.py +34 -0
- argus/backend/models/__init__.py +0 -0
- argus/backend/models/argus_ai.py +24 -0
- argus/backend/models/github_issue.py +60 -0
- argus/backend/models/plan.py +24 -0
- argus/backend/models/result.py +187 -0
- argus/backend/models/runtime_store.py +58 -0
- argus/backend/models/view_widgets.py +25 -0
- argus/backend/models/web.py +403 -0
- argus/backend/plugins/__init__.py +0 -0
- argus/backend/plugins/core.py +248 -0
- argus/backend/plugins/driver_matrix_tests/controller.py +66 -0
- argus/backend/plugins/driver_matrix_tests/model.py +429 -0
- argus/backend/plugins/driver_matrix_tests/plugin.py +21 -0
- argus/backend/plugins/driver_matrix_tests/raw_types.py +62 -0
- argus/backend/plugins/driver_matrix_tests/service.py +61 -0
- argus/backend/plugins/driver_matrix_tests/udt.py +42 -0
- argus/backend/plugins/generic/model.py +86 -0
- argus/backend/plugins/generic/plugin.py +15 -0
- argus/backend/plugins/generic/types.py +14 -0
- argus/backend/plugins/loader.py +39 -0
- argus/backend/plugins/sct/controller.py +224 -0
- argus/backend/plugins/sct/plugin.py +37 -0
- argus/backend/plugins/sct/resource_setup.py +177 -0
- argus/backend/plugins/sct/service.py +682 -0
- argus/backend/plugins/sct/testrun.py +288 -0
- argus/backend/plugins/sct/udt.py +100 -0
- argus/backend/plugins/sirenada/model.py +118 -0
- argus/backend/plugins/sirenada/plugin.py +16 -0
- argus/backend/service/admin.py +26 -0
- argus/backend/service/argus_service.py +696 -0
- argus/backend/service/build_system_monitor.py +185 -0
- argus/backend/service/client_service.py +127 -0
- argus/backend/service/event_service.py +18 -0
- argus/backend/service/github_service.py +233 -0
- argus/backend/service/jenkins_service.py +269 -0
- argus/backend/service/notification_manager.py +159 -0
- argus/backend/service/planner_service.py +608 -0
- argus/backend/service/release_manager.py +229 -0
- argus/backend/service/results_service.py +690 -0
- argus/backend/service/stats.py +610 -0
- argus/backend/service/team_manager_service.py +82 -0
- argus/backend/service/test_lookup.py +172 -0
- argus/backend/service/testrun.py +489 -0
- argus/backend/service/user.py +308 -0
- argus/backend/service/views.py +219 -0
- argus/backend/service/views_widgets/__init__.py +0 -0
- argus/backend/service/views_widgets/graphed_stats.py +180 -0
- argus/backend/service/views_widgets/highlights.py +374 -0
- argus/backend/service/views_widgets/nemesis_stats.py +34 -0
- argus/backend/template_filters.py +27 -0
- argus/backend/tests/__init__.py +0 -0
- argus/backend/tests/client_service/__init__.py +0 -0
- argus/backend/tests/client_service/test_submit_results.py +79 -0
- argus/backend/tests/conftest.py +180 -0
- argus/backend/tests/results_service/__init__.py +0 -0
- argus/backend/tests/results_service/test_best_results.py +178 -0
- argus/backend/tests/results_service/test_cell.py +65 -0
- argus/backend/tests/results_service/test_chartjs_additional_functions.py +259 -0
- argus/backend/tests/results_service/test_create_chartjs.py +220 -0
- argus/backend/tests/results_service/test_result_metadata.py +100 -0
- argus/backend/tests/results_service/test_results_service.py +203 -0
- argus/backend/tests/results_service/test_validation_rules.py +213 -0
- argus/backend/tests/view_widgets/__init__.py +0 -0
- argus/backend/tests/view_widgets/test_highlights_api.py +532 -0
- argus/backend/util/common.py +65 -0
- argus/backend/util/config.py +38 -0
- argus/backend/util/encoders.py +56 -0
- argus/backend/util/logsetup.py +80 -0
- argus/backend/util/module_loaders.py +30 -0
- argus/backend/util/send_email.py +91 -0
- argus/client/base.py +1 -3
- argus/client/driver_matrix_tests/cli.py +17 -8
- argus/client/generic/cli.py +4 -2
- argus/client/generic/client.py +1 -0
- argus/client/generic_result.py +48 -9
- argus/client/sct/client.py +1 -3
- argus/client/sirenada/client.py +4 -1
- argus/client/tests/__init__.py +0 -0
- argus/client/tests/conftest.py +19 -0
- argus/client/tests/test_package.py +45 -0
- argus/client/tests/test_results.py +224 -0
- argus/common/sct_types.py +3 -0
- argus/common/sirenada_types.py +1 -1
- {argus_alm-0.14.2.dist-info → argus_alm-0.15.2.dist-info}/METADATA +43 -19
- argus_alm-0.15.2.dist-info/RECORD +122 -0
- {argus_alm-0.14.2.dist-info → argus_alm-0.15.2.dist-info}/WHEEL +2 -1
- argus_alm-0.15.2.dist-info/entry_points.txt +3 -0
- argus_alm-0.15.2.dist-info/top_level.txt +1 -0
- argus_alm-0.14.2.dist-info/RECORD +0 -20
- argus_alm-0.14.2.dist-info/entry_points.txt +0 -4
- {argus_alm-0.14.2.dist-info → argus_alm-0.15.2.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
import re
|
|
3
|
+
from uuid import UUID
|
|
4
|
+
from cassandra.cqlengine import columns
|
|
5
|
+
from cassandra.cqlengine.models import Model
|
|
6
|
+
from argus.backend.db import ScyllaCluster
|
|
7
|
+
from argus.backend.models.web import ArgusRelease
|
|
8
|
+
from argus.backend.plugins.core import PluginModelBase
|
|
9
|
+
from argus.backend.plugins.generic.types import GenericRunFinishRequest, GenericRunSubmitRequest
|
|
10
|
+
from argus.common.enums import TestStatus
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class GenericPluginException(Exception):
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class GenericRun(PluginModelBase):
|
|
18
|
+
_plugin_name = "generic"
|
|
19
|
+
__table_name__ = "generic_run"
|
|
20
|
+
logs = columns.Map(key_type=columns.Text(), value_type=columns.Text())
|
|
21
|
+
started_by = columns.Text()
|
|
22
|
+
# TODO: Legacy field name, should be renamed to product_version and abstracted
|
|
23
|
+
scylla_version = columns.Text()
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def _stats_query(cls) -> str:
|
|
27
|
+
return ("SELECT id, test_id, group_id, release_id, status, start_time, build_job_url, build_id, "
|
|
28
|
+
f"assignee, end_time, investigation_status, heartbeat, scylla_version FROM {cls.table_name()} WHERE build_id IN ? PER PARTITION LIMIT 15")
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def get_distinct_product_versions(cls, release: ArgusRelease, cluster: ScyllaCluster = None) -> list[str]:
|
|
32
|
+
if not cluster:
|
|
33
|
+
cluster = ScyllaCluster.get()
|
|
34
|
+
statement = cluster.prepare(f"SELECT scylla_version FROM {cls.table_name()} WHERE release_id = ?")
|
|
35
|
+
rows = cluster.session.execute(query=statement, parameters=(release.id,))
|
|
36
|
+
unique_versions = {r["scylla_version"] for r in rows if r["scylla_version"]}
|
|
37
|
+
|
|
38
|
+
return sorted(list(unique_versions), reverse=True)
|
|
39
|
+
|
|
40
|
+
def submit_product_version(self, version: str):
|
|
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
|
+
try:
|
|
45
|
+
new_assignee = self.get_assignment(match.group("short"))
|
|
46
|
+
except Model.DoesNotExist:
|
|
47
|
+
new_assignee = None
|
|
48
|
+
if new_assignee:
|
|
49
|
+
self.assignee = new_assignee
|
|
50
|
+
self.set_full_version(version)
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def load_test_run(cls, run_id: UUID) -> 'GenericRun':
|
|
54
|
+
return cls.get(id=run_id)
|
|
55
|
+
|
|
56
|
+
@classmethod
|
|
57
|
+
def submit_run(cls, request_data: GenericRunSubmitRequest) -> 'GenericRun':
|
|
58
|
+
try:
|
|
59
|
+
run = cls.get(id=request_data["run_id"])
|
|
60
|
+
raise GenericPluginException(
|
|
61
|
+
f"Run with UUID {request_data['run_id']} already exists.", request_data["run_id"])
|
|
62
|
+
except cls.DoesNotExist:
|
|
63
|
+
pass
|
|
64
|
+
run = cls()
|
|
65
|
+
run.start_time = datetime.utcnow()
|
|
66
|
+
run.build_id = request_data["build_id"]
|
|
67
|
+
run.started_by = request_data["started_by"]
|
|
68
|
+
run.id = request_data["run_id"]
|
|
69
|
+
run.build_job_url = request_data["build_url"]
|
|
70
|
+
run.assign_categories()
|
|
71
|
+
try:
|
|
72
|
+
run.assignee = run.get_scheduled_assignee()
|
|
73
|
+
except Model.DoesNotExist:
|
|
74
|
+
run.assignee = None
|
|
75
|
+
if version := request_data.get("scylla_version"):
|
|
76
|
+
run.submit_product_version(version)
|
|
77
|
+
run.status = TestStatus.RUNNING.value
|
|
78
|
+
run.save()
|
|
79
|
+
|
|
80
|
+
return run
|
|
81
|
+
|
|
82
|
+
def finish_run(self, payload: GenericRunFinishRequest = None):
|
|
83
|
+
self.end_time = datetime.utcnow()
|
|
84
|
+
self.status = TestStatus(payload["status"]).value
|
|
85
|
+
if version := payload.get("scylla_version"):
|
|
86
|
+
self.submit_product_version(version)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from flask import Blueprint
|
|
2
|
+
|
|
3
|
+
from argus.backend.plugins.core import PluginInfoBase, PluginModelBase
|
|
4
|
+
from argus.backend.plugins.generic.model import GenericRun
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class PluginInfo(PluginInfoBase):
|
|
8
|
+
name: str = "generic"
|
|
9
|
+
model: PluginModelBase = GenericRun
|
|
10
|
+
controller: Blueprint = None
|
|
11
|
+
all_models = [
|
|
12
|
+
GenericRun
|
|
13
|
+
]
|
|
14
|
+
all_types = [
|
|
15
|
+
]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from typing import TypedDict
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class GenericRunSubmitRequest(TypedDict):
|
|
5
|
+
build_id: str
|
|
6
|
+
build_url: str
|
|
7
|
+
run_id: str
|
|
8
|
+
started_by: str
|
|
9
|
+
scylla_version: str | None
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class GenericRunFinishRequest(TypedDict):
|
|
13
|
+
status: str
|
|
14
|
+
scylla_version: str | None
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import typing
|
|
3
|
+
from argus.backend.plugins.core import PluginInfoBase
|
|
4
|
+
from argus.backend.plugins.core import PluginModelBase
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class PluginModule(typing.Protocol):
|
|
8
|
+
PluginInfo: PluginInfoBase
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def plugin_loader() -> dict[str, PluginInfoBase]:
|
|
12
|
+
loader_path = Path(__file__).parent
|
|
13
|
+
plugin_dirs = [p for p in loader_path.glob("*") if p.is_dir()]
|
|
14
|
+
modules = {}
|
|
15
|
+
parent_module = ".".join(__name__.split(".")[:-1])
|
|
16
|
+
for directory in plugin_dirs:
|
|
17
|
+
if (plugin_path := directory / "plugin.py").exists():
|
|
18
|
+
rel_path = str(plugin_path.relative_to(loader_path))
|
|
19
|
+
module_path = rel_path.replace("/", ".").replace(".py", "")
|
|
20
|
+
module: PluginModule = __import__(
|
|
21
|
+
f"{parent_module}.{module_path}",
|
|
22
|
+
globals=globals(),
|
|
23
|
+
fromlist=("PluginInfo",)
|
|
24
|
+
)
|
|
25
|
+
plugin = module.PluginInfo
|
|
26
|
+
modules[plugin.name] = plugin
|
|
27
|
+
|
|
28
|
+
return modules
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
AVAILABLE_PLUGINS = plugin_loader()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def all_plugin_models(include_all=False) -> list[PluginModelBase]:
|
|
35
|
+
return [model for plugin in AVAILABLE_PLUGINS.values() for model in plugin.all_models if issubclass(model, PluginModelBase) or include_all]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def all_plugin_types():
|
|
39
|
+
return [user_type for plugin in AVAILABLE_PLUGINS.values() for user_type in plugin.all_types]
|
|
@@ -0,0 +1,224 @@
|
|
|
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.sct.service import SCTService
|
|
6
|
+
from argus.backend.util.common import get_payload
|
|
7
|
+
|
|
8
|
+
bp = Blueprint("sct_api", __name__, url_prefix="/sct")
|
|
9
|
+
bp.register_error_handler(Exception, handle_api_exception)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@bp.route("/<string:run_id>/packages/submit", methods=["POST"])
|
|
13
|
+
@api_login_required
|
|
14
|
+
def sct_submit_packages(run_id: str):
|
|
15
|
+
payload = get_payload(request)
|
|
16
|
+
result = SCTService.submit_packages(run_id=run_id, packages=payload["packages"])
|
|
17
|
+
return {
|
|
18
|
+
"status": "ok",
|
|
19
|
+
"response": result
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@bp.route("/<string:run_id>/screenshots/submit", methods=["POST"])
|
|
24
|
+
@api_login_required
|
|
25
|
+
def sct_submit_screenshots(run_id: str):
|
|
26
|
+
payload = get_payload(request)
|
|
27
|
+
result = SCTService.submit_screenshots(run_id=run_id, screenshot_links=payload["screenshot_links"])
|
|
28
|
+
return {
|
|
29
|
+
"status": "ok",
|
|
30
|
+
"response": result
|
|
31
|
+
}
|
|
32
|
+
|
|
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
|
+
name=payload.get("name")
|
|
45
|
+
)
|
|
46
|
+
return {
|
|
47
|
+
"status": "ok",
|
|
48
|
+
"response": result
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@bp.route("/<string:run_id>/resource/create", methods=["POST"])
|
|
53
|
+
@api_login_required
|
|
54
|
+
def sct_resource_create(run_id: str):
|
|
55
|
+
payload = get_payload(request)
|
|
56
|
+
result = SCTService.create_resource(run_id=run_id, resource_details=payload["resource"])
|
|
57
|
+
return {
|
|
58
|
+
"status": "ok",
|
|
59
|
+
"response": result
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@bp.route("/<string:run_id>/resource/<string:resource_name>/terminate", methods=["POST"])
|
|
64
|
+
@api_login_required
|
|
65
|
+
def sct_resource_terminate(run_id: str, resource_name: str):
|
|
66
|
+
payload = get_payload(request)
|
|
67
|
+
result = SCTService.terminate_resource(run_id=run_id, resource_name=resource_name, reason=payload["reason"])
|
|
68
|
+
return {
|
|
69
|
+
"status": "ok",
|
|
70
|
+
"response": result
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@bp.route("/<string:run_id>/resource/<string:resource_name>/shards", methods=["POST"])
|
|
75
|
+
@api_login_required
|
|
76
|
+
def sct_resource_update_shards(run_id: str, resource_name: str):
|
|
77
|
+
payload = get_payload(request)
|
|
78
|
+
result = SCTService.update_resource_shards(run_id=run_id, resource_name=resource_name, new_shards=payload["shards"])
|
|
79
|
+
return {
|
|
80
|
+
"status": "ok",
|
|
81
|
+
"response": result
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@bp.route("/<string:run_id>/resource/<string:resource_name>/update", methods=["POST"])
|
|
86
|
+
@api_login_required
|
|
87
|
+
def sct_resource_update(run_id: str, resource_name: str):
|
|
88
|
+
payload = get_payload(request)
|
|
89
|
+
result = SCTService.update_resource(run_id=run_id, resource_name=resource_name, update_data=payload["update_data"])
|
|
90
|
+
return {
|
|
91
|
+
"status": "ok",
|
|
92
|
+
"response": result
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@bp.route("/<string:run_id>/nemesis/submit", methods=["POST"])
|
|
97
|
+
@api_login_required
|
|
98
|
+
def sct_nemesis_submit(run_id: str):
|
|
99
|
+
payload = get_payload(request)
|
|
100
|
+
result = SCTService.submit_nemesis(run_id=run_id, nemesis_details=payload["nemesis"])
|
|
101
|
+
return {
|
|
102
|
+
"status": "ok",
|
|
103
|
+
"response": result
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@bp.route("/<string:run_id>/nemesis/finalize", methods=["POST"])
|
|
108
|
+
@api_login_required
|
|
109
|
+
def sct_nemesis_finalize(run_id: str):
|
|
110
|
+
payload = get_payload(request)
|
|
111
|
+
result = SCTService.finalize_nemesis(run_id=run_id, nemesis_details=payload["nemesis"])
|
|
112
|
+
return {
|
|
113
|
+
"status": "ok",
|
|
114
|
+
"response": result
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@bp.route("/<string:run_id>/events/submit", methods=["POST"])
|
|
119
|
+
@api_login_required
|
|
120
|
+
def sct_events_submit(run_id: str):
|
|
121
|
+
payload = get_payload(request)
|
|
122
|
+
result = SCTService.submit_events(run_id=run_id, events=payload["events"])
|
|
123
|
+
return {
|
|
124
|
+
"status": "ok",
|
|
125
|
+
"response": result
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@bp.route("/<string:run_id>/gemini/submit", methods=["POST"])
|
|
130
|
+
@api_login_required
|
|
131
|
+
def sct_gemini_results_submit(run_id: str):
|
|
132
|
+
payload = get_payload(request)
|
|
133
|
+
result = SCTService.submit_gemini_results(run_id=run_id, gemini_data=payload["gemini_data"])
|
|
134
|
+
return {
|
|
135
|
+
"status": "ok",
|
|
136
|
+
"response": result
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@bp.route("/<string:run_id>/performance/submit", methods=["POST"])
|
|
141
|
+
@api_login_required
|
|
142
|
+
def sct_performance_results_submit(run_id: str):
|
|
143
|
+
payload = get_payload(request)
|
|
144
|
+
result = SCTService.submit_performance_results(run_id=run_id, performance_results=payload["performance_results"])
|
|
145
|
+
return {
|
|
146
|
+
"status": "ok",
|
|
147
|
+
"response": result
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@bp.route("/<string:run_id>/performance/history", methods=["GET"])
|
|
152
|
+
@api_login_required
|
|
153
|
+
def sct_get_performance_history(run_id: str):
|
|
154
|
+
result = SCTService.get_performance_history_for_test(run_id=run_id)
|
|
155
|
+
return {
|
|
156
|
+
"status": "ok",
|
|
157
|
+
"response": result
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@bp.route("/release/<string:release_name>/kernels", methods=["GET"])
|
|
162
|
+
@api_login_required
|
|
163
|
+
def sct_get_kernel_report(release_name: str):
|
|
164
|
+
result = SCTService.get_scylla_version_kernels_report(release_name=release_name)
|
|
165
|
+
return {
|
|
166
|
+
"status": "ok",
|
|
167
|
+
"response": result
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@bp.route("/<string:run_id>/junit/submit", methods=["POST"])
|
|
172
|
+
@api_login_required
|
|
173
|
+
def sct_submit_junit_report(run_id: str):
|
|
174
|
+
payload = get_payload(request)
|
|
175
|
+
result = SCTService.junit_submit(run_id, payload["file_name"], payload["content"])
|
|
176
|
+
return {
|
|
177
|
+
"status": "ok",
|
|
178
|
+
"response": result
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@bp.route("/<string:run_id>/junit/get_all", methods=["GET"])
|
|
183
|
+
@api_login_required
|
|
184
|
+
def sct_get_junit_reports(run_id: str):
|
|
185
|
+
result = SCTService.junit_get_all(run_id)
|
|
186
|
+
return {
|
|
187
|
+
"status": "ok",
|
|
188
|
+
"response": result
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@bp.route("/<string:run_id>/similar_events", methods=["GET"])
|
|
193
|
+
@api_login_required
|
|
194
|
+
def sct_get_similar_events(run_id: str):
|
|
195
|
+
result = SCTService.get_similar_events(run_id=run_id)
|
|
196
|
+
return {
|
|
197
|
+
"status": "ok",
|
|
198
|
+
"response": result
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@bp.route("/similar_runs_info", methods=["POST"])
|
|
203
|
+
@api_login_required
|
|
204
|
+
def sct_get_similar_runs_info():
|
|
205
|
+
"""Get build IDs and issues for a list of run IDs"""
|
|
206
|
+
data = request.get_json()
|
|
207
|
+
if not data or "run_ids" not in data:
|
|
208
|
+
return {
|
|
209
|
+
"status": "error",
|
|
210
|
+
"response": "Missing run_ids parameter"
|
|
211
|
+
}, 400
|
|
212
|
+
|
|
213
|
+
run_ids = data["run_ids"]
|
|
214
|
+
if not isinstance(run_ids, list):
|
|
215
|
+
return {
|
|
216
|
+
"status": "error",
|
|
217
|
+
"response": "run_ids must be a list"
|
|
218
|
+
}, 400
|
|
219
|
+
|
|
220
|
+
result = SCTService.get_similar_runs_info(run_ids=run_ids)
|
|
221
|
+
return {
|
|
222
|
+
"status": "ok",
|
|
223
|
+
"response": result
|
|
224
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from flask import Blueprint
|
|
2
|
+
|
|
3
|
+
from argus.backend.plugins.sct.testrun import SCTJunitReports, SCTTestRun
|
|
4
|
+
from argus.backend.plugins.sct.controller import bp as sct_bp
|
|
5
|
+
from argus.backend.plugins.core import PluginInfoBase, PluginModelBase
|
|
6
|
+
from argus.backend.plugins.sct.udt import (
|
|
7
|
+
CloudInstanceDetails,
|
|
8
|
+
CloudNodesInfo,
|
|
9
|
+
CloudResource,
|
|
10
|
+
CloudSetupDetails,
|
|
11
|
+
EventsBySeverity,
|
|
12
|
+
NemesisRunInfo,
|
|
13
|
+
NodeDescription,
|
|
14
|
+
PackageVersion,
|
|
15
|
+
PerformanceHDRHistogram,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PluginInfo(PluginInfoBase):
|
|
20
|
+
name: str = "scylla-cluster-tests"
|
|
21
|
+
model: PluginModelBase = SCTTestRun
|
|
22
|
+
controller: Blueprint = sct_bp
|
|
23
|
+
all_models = [
|
|
24
|
+
SCTTestRun,
|
|
25
|
+
SCTJunitReports,
|
|
26
|
+
]
|
|
27
|
+
all_types = [
|
|
28
|
+
NemesisRunInfo,
|
|
29
|
+
NodeDescription,
|
|
30
|
+
EventsBySeverity,
|
|
31
|
+
CloudResource,
|
|
32
|
+
CloudSetupDetails,
|
|
33
|
+
CloudNodesInfo,
|
|
34
|
+
CloudInstanceDetails,
|
|
35
|
+
PackageVersion,
|
|
36
|
+
PerformanceHDRHistogram,
|
|
37
|
+
]
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from argus.backend.plugins.sct.udt import CloudNodesInfo, CloudSetupDetails
|
|
3
|
+
|
|
4
|
+
LOGGER = logging.getLogger(__name__)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _get_node_amounts(config: dict) -> tuple[int, int]:
|
|
8
|
+
num_db_node = config.get("n_db_nodes")
|
|
9
|
+
num_db_node = sum([int(i) for i in num_db_node.split()]) if isinstance(num_db_node, str) else num_db_node
|
|
10
|
+
num_loaders = config.get("n_loaders")
|
|
11
|
+
num_loaders = sum([int(i) for i in num_loaders.split()]) if isinstance(num_loaders, str) else num_loaders
|
|
12
|
+
|
|
13
|
+
return num_db_node, num_loaders
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _prepare_aws_resource_setup(sct_config: dict) -> CloudSetupDetails:
|
|
17
|
+
num_db_nodes, n_loaders = _get_node_amounts(sct_config)
|
|
18
|
+
db_node_setup = CloudNodesInfo(image_id=sct_config.get("ami_id_db_scylla"),
|
|
19
|
+
instance_type=sct_config.get("instance_type_db"),
|
|
20
|
+
node_amount=num_db_nodes,
|
|
21
|
+
post_behaviour=sct_config.get("post_behavior_db_nodes"))
|
|
22
|
+
loader_node_setup = CloudNodesInfo(image_id=sct_config.get("ami_id_loader"),
|
|
23
|
+
instance_type=sct_config.get("instance_type_loader"),
|
|
24
|
+
node_amount=n_loaders,
|
|
25
|
+
post_behaviour=sct_config.get("post_behavior_loader_nodes"))
|
|
26
|
+
monitor_node_setup = CloudNodesInfo(image_id=sct_config.get("ami_id_monitor"),
|
|
27
|
+
instance_type=sct_config.get("instance_type_monitor"),
|
|
28
|
+
node_amount=sct_config.get("n_monitor_nodes"),
|
|
29
|
+
post_behaviour=sct_config.get("post_behavior_monitor_nodes"))
|
|
30
|
+
cloud_setup = CloudSetupDetails(db_node=db_node_setup, loader_node=loader_node_setup,
|
|
31
|
+
monitor_node=monitor_node_setup, backend=sct_config.get("cluster_backend"))
|
|
32
|
+
|
|
33
|
+
return cloud_setup
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _prepare_gce_resource_setup(sct_config: dict) -> CloudSetupDetails:
|
|
37
|
+
num_db_nodes, n_loaders = _get_node_amounts(sct_config)
|
|
38
|
+
db_node_setup = CloudNodesInfo(image_id=sct_config.get("gce_image_db"),
|
|
39
|
+
instance_type=sct_config.get("gce_instance_type_db"),
|
|
40
|
+
node_amount=num_db_nodes,
|
|
41
|
+
post_behaviour=sct_config.get("post_behavior_db_nodes"))
|
|
42
|
+
loader_node_setup = CloudNodesInfo(image_id=sct_config.get("gce_image_loader"),
|
|
43
|
+
instance_type=sct_config.get("gce_instance_type_loader"),
|
|
44
|
+
node_amount=n_loaders,
|
|
45
|
+
post_behaviour=sct_config.get("post_behavior_loader_nodes"))
|
|
46
|
+
monitor_node_setup = CloudNodesInfo(image_id=sct_config.get("gce_image_monitor"),
|
|
47
|
+
instance_type=sct_config.get("gce_instance_type_monitor"),
|
|
48
|
+
node_amount=sct_config.get("n_monitor_nodes"),
|
|
49
|
+
post_behaviour=sct_config.get("post_behavior_monitor_nodes"))
|
|
50
|
+
cloud_setup = CloudSetupDetails(db_node=db_node_setup, loader_node=loader_node_setup,
|
|
51
|
+
monitor_node=monitor_node_setup, backend=sct_config.get("cluster_backend"))
|
|
52
|
+
|
|
53
|
+
return cloud_setup
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _prepare_azure_resource_setup(sct_config: dict) -> CloudSetupDetails:
|
|
57
|
+
num_db_nodes, n_loaders = _get_node_amounts(sct_config)
|
|
58
|
+
db_node_setup = CloudNodesInfo(image_id=sct_config.get("azure_image_db"),
|
|
59
|
+
instance_type=sct_config.get("azure_instance_type_db"),
|
|
60
|
+
node_amount=num_db_nodes,
|
|
61
|
+
post_behaviour=sct_config.get("post_behavior_db_nodes"))
|
|
62
|
+
loader_node_setup = CloudNodesInfo(image_id=sct_config.get("azure_image_loader"),
|
|
63
|
+
instance_type=sct_config.get("azure_instance_type_loader"),
|
|
64
|
+
node_amount=n_loaders,
|
|
65
|
+
post_behaviour=sct_config.get("post_behavior_loader_nodes"))
|
|
66
|
+
monitor_node_setup = CloudNodesInfo(image_id=sct_config.get("azure_image_monitor"),
|
|
67
|
+
instance_type=sct_config.get("azure_instance_type_monitor"),
|
|
68
|
+
node_amount=sct_config.get("n_monitor_nodes"),
|
|
69
|
+
post_behaviour=sct_config.get("post_behavior_monitor_nodes"))
|
|
70
|
+
cloud_setup = CloudSetupDetails(db_node=db_node_setup, loader_node=loader_node_setup,
|
|
71
|
+
monitor_node=monitor_node_setup, backend=sct_config.get("cluster_backend"))
|
|
72
|
+
|
|
73
|
+
return cloud_setup
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _prepare_unknown_resource_setup(sct_config: dict) -> CloudSetupDetails:
|
|
77
|
+
LOGGER.error("Unknown backend encountered: %s", sct_config.get("cluster_backend"))
|
|
78
|
+
db_node_setup = CloudNodesInfo(image_id="UNKNOWN",
|
|
79
|
+
instance_type="UNKNOWN",
|
|
80
|
+
node_amount=-1,
|
|
81
|
+
post_behaviour="UNKNOWN")
|
|
82
|
+
loader_node_setup = CloudNodesInfo(image_id="UNKNOWN",
|
|
83
|
+
instance_type="UNKNOWN",
|
|
84
|
+
node_amount=-1,
|
|
85
|
+
post_behaviour="UNKNOWN")
|
|
86
|
+
monitor_node_setup = CloudNodesInfo(image_id="UNKNOWN",
|
|
87
|
+
instance_type="UNKNOWN",
|
|
88
|
+
node_amount=-1,
|
|
89
|
+
post_behaviour="UNKNOWN")
|
|
90
|
+
cloud_setup = CloudSetupDetails(db_node=db_node_setup, loader_node=loader_node_setup,
|
|
91
|
+
monitor_node=monitor_node_setup, backend=sct_config.get("cluster_backend"))
|
|
92
|
+
|
|
93
|
+
return cloud_setup
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _prepare_bare_metal_resource_setup(sct_config: dict) -> CloudSetupDetails:
|
|
97
|
+
db_node_setup = CloudNodesInfo(image_id="bare_metal",
|
|
98
|
+
instance_type="bare_metal",
|
|
99
|
+
node_amount=sct_config.get("n_db_nodes"),
|
|
100
|
+
post_behaviour=sct_config.get("post_behavior_db_nodes"))
|
|
101
|
+
loader_node_setup = CloudNodesInfo(image_id="bare_metal",
|
|
102
|
+
instance_type="bare_metal",
|
|
103
|
+
node_amount=sct_config.get("n_loaders"),
|
|
104
|
+
post_behaviour=sct_config.get("post_behavior_loader_nodes"))
|
|
105
|
+
monitor_node_setup = CloudNodesInfo(image_id="bare_metal",
|
|
106
|
+
instance_type="bare_metal",
|
|
107
|
+
node_amount=sct_config.get("n_monitor_nodes"),
|
|
108
|
+
post_behaviour=sct_config.get("post_behavior_monitor_nodes"))
|
|
109
|
+
cloud_setup = CloudSetupDetails(db_node=db_node_setup, loader_node=loader_node_setup,
|
|
110
|
+
monitor_node=monitor_node_setup, backend=sct_config.get("cluster_backend"))
|
|
111
|
+
|
|
112
|
+
return cloud_setup
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _prepare_k8s_gce_minikube_resource_setup(sct_config: dict) -> CloudSetupDetails:
|
|
116
|
+
cloud_setup = _prepare_gce_resource_setup(sct_config)
|
|
117
|
+
|
|
118
|
+
image_id = sct_config.get("scylla_version")
|
|
119
|
+
cloud_setup.db_node.image_id = f"scylladb/scylladb:{image_id}"
|
|
120
|
+
cloud_setup.db_node.instance_type = sct_config.get("gce_instance_type_minikube")
|
|
121
|
+
|
|
122
|
+
return cloud_setup
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _prepare_k8s_gke_resource_setup(sct_config: dict) -> CloudSetupDetails:
|
|
126
|
+
cloud_setup = _prepare_gce_resource_setup(sct_config)
|
|
127
|
+
image_id = sct_config.get("scylla_version")
|
|
128
|
+
cloud_setup.db_node.image_id = f"scylladb/scylladb:{image_id}"
|
|
129
|
+
cloud_setup.monitor_node.image_id = sct_config.get("mgmt_docker_image")
|
|
130
|
+
cloud_setup.loader_node.image_id = f"scylladb/scylladb:{image_id}"
|
|
131
|
+
|
|
132
|
+
return cloud_setup
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _prepare_k8s_eks_resource_setup(sct_config: dict) -> CloudSetupDetails:
|
|
136
|
+
cloud_setup = _prepare_aws_resource_setup(sct_config)
|
|
137
|
+
|
|
138
|
+
return cloud_setup
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _prepare_docker_resource_setup(sct_config: dict) -> CloudSetupDetails:
|
|
142
|
+
db_node_setup = CloudNodesInfo(image_id=sct_config.get('docker_image'),
|
|
143
|
+
instance_type="docker",
|
|
144
|
+
node_amount=sct_config.get("n_db_nodes"),
|
|
145
|
+
post_behaviour=sct_config.get("post_behavior_db_nodes"))
|
|
146
|
+
loader_node_setup = CloudNodesInfo(image_id=sct_config.get('docker_image'),
|
|
147
|
+
instance_type="docker",
|
|
148
|
+
node_amount=sct_config.get("n_loaders"),
|
|
149
|
+
post_behaviour=sct_config.get("post_behavior_loader_nodes"))
|
|
150
|
+
monitor_node_setup = CloudNodesInfo(image_id=sct_config.get('docker_image'),
|
|
151
|
+
instance_type="docker",
|
|
152
|
+
node_amount=sct_config.get("n_monitor_nodes"),
|
|
153
|
+
post_behaviour=sct_config.get("post_behavior_monitor_nodes"))
|
|
154
|
+
cloud_setup = CloudSetupDetails(db_node=db_node_setup, loader_node=loader_node_setup,
|
|
155
|
+
monitor_node=monitor_node_setup, backend=sct_config.get("cluster_backend"))
|
|
156
|
+
|
|
157
|
+
return cloud_setup
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class ResourceSetup:
|
|
161
|
+
BACKEND_MAP = {
|
|
162
|
+
"aws": _prepare_aws_resource_setup,
|
|
163
|
+
"aws-siren": _prepare_aws_resource_setup,
|
|
164
|
+
"azure": _prepare_azure_resource_setup,
|
|
165
|
+
"gce": _prepare_gce_resource_setup,
|
|
166
|
+
"gce-siren": _prepare_gce_resource_setup,
|
|
167
|
+
"k8s-eks": _prepare_k8s_eks_resource_setup,
|
|
168
|
+
"k8s-gke": _prepare_k8s_gke_resource_setup,
|
|
169
|
+
"k8s-gce-minikube": _prepare_k8s_gce_minikube_resource_setup,
|
|
170
|
+
"baremetal": _prepare_bare_metal_resource_setup,
|
|
171
|
+
"docker": _prepare_docker_resource_setup,
|
|
172
|
+
"unknown": _prepare_unknown_resource_setup,
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
@classmethod
|
|
176
|
+
def get_resource_setup(cls, backend: str, sct_config: dict) -> CloudSetupDetails:
|
|
177
|
+
return cls.BACKEND_MAP.get(backend, _prepare_unknown_resource_setup)(sct_config)
|