argus-alm 0.12.7__tar.gz → 0.12.9__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.12.7 → argus_alm-0.12.9}/PKG-INFO +1 -1
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/cli.py +3 -1
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/controller/api.py +7 -4
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/controller/client_api.py +1 -5
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/controller/testrun_api.py +2 -1
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/db.py +1 -1
- argus_alm-0.12.9/argus/backend/models/result.py +138 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/models/web.py +2 -1
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/plugins/loader.py +1 -1
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/plugins/sct/testrun.py +10 -3
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/service/client_service.py +31 -10
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/service/notification_manager.py +4 -2
- argus_alm-0.12.9/argus/backend/service/results_service.py +317 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/service/testrun.py +16 -22
- argus_alm-0.12.9/argus/backend/tests/argus_web.test.yaml +39 -0
- argus_alm-0.12.9/argus/backend/tests/conftest.py +44 -0
- argus_alm-0.12.9/argus/backend/tests/results_service/__init__.py +0 -0
- argus_alm-0.12.9/argus/backend/tests/results_service/test_best_results.py +70 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/client/generic_result.py +36 -7
- argus_alm-0.12.9/argus/client/generic_result_old.py +143 -0
- argus_alm-0.12.9/argus/db/.gitkeep +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/pyproject.toml +2 -2
- argus_alm-0.12.7/argus/backend/models/result.py +0 -63
- argus_alm-0.12.7/argus/backend/service/results_service.py +0 -140
- {argus_alm-0.12.7 → argus_alm-0.12.9}/LICENSE +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/README.md +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/__init__.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/.gitkeep +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/__init__.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/controller/__init__.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/controller/admin.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/controller/admin_api.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/controller/auth.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/controller/main.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/controller/notification_api.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/controller/notifications.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/controller/team.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/controller/team_ui.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/controller/view_api.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/error_handlers.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/events/event_processors.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/models/__init__.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/plugins/__init__.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/plugins/core.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/plugins/driver_matrix_tests/controller.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/plugins/driver_matrix_tests/model.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/plugins/driver_matrix_tests/plugin.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/plugins/driver_matrix_tests/raw_types.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/plugins/driver_matrix_tests/service.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/plugins/driver_matrix_tests/udt.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/plugins/generic/model.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/plugins/generic/plugin.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/plugins/generic/types.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/plugins/sct/controller.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/plugins/sct/plugin.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/plugins/sct/resource_setup.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/plugins/sct/service.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/plugins/sct/types.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/plugins/sct/udt.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/plugins/sirenada/model.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/plugins/sirenada/plugin.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/plugins/sirenada/types.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/service/admin.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/service/argus_service.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/service/build_system_monitor.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/service/event_service.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/service/jenkins_service.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/service/release_manager.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/service/stats.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/service/team_manager_service.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/service/user.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/service/views.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/template_filters.py +0 -0
- /argus_alm-0.12.7/argus/db/.gitkeep → /argus_alm-0.12.9/argus/backend/tests/__init__.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/util/common.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/util/config.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/util/encoders.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/util/enums.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/util/logsetup.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/util/module_loaders.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/backend/util/send_email.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/client/__init__.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/client/base.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/client/driver_matrix_tests/cli.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/client/driver_matrix_tests/client.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/client/generic/cli.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/client/generic/client.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/client/sct/client.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/client/sct/types.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/client/sirenada/client.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/db/argus_json.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/db/cloud_types.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/db/config.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/db/db_types.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/db/interface.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/db/testrun.py +0 -0
- {argus_alm-0.12.7 → argus_alm-0.12.9}/argus/db/utils.py +0 -0
|
@@ -15,6 +15,9 @@ LOGGER = logging.getLogger(__name__)
|
|
|
15
15
|
@click.command('sync-models')
|
|
16
16
|
@with_appcontext
|
|
17
17
|
def sync_models_command():
|
|
18
|
+
sync_models()
|
|
19
|
+
|
|
20
|
+
def sync_models():
|
|
18
21
|
cluster = ScyllaCluster.get()
|
|
19
22
|
cluster.sync_core_tables()
|
|
20
23
|
LOGGER.info("Synchronizing plugin types...")
|
|
@@ -29,7 +32,6 @@ def sync_models_command():
|
|
|
29
32
|
LOGGER.info("Plugins ready.")
|
|
30
33
|
click.echo("All models synchronized.")
|
|
31
34
|
|
|
32
|
-
|
|
33
35
|
@cli_bp.cli.add_command
|
|
34
36
|
@click.command('scan-jenkins')
|
|
35
37
|
@with_appcontext
|
|
@@ -4,7 +4,7 @@ import requests
|
|
|
4
4
|
from flask import (
|
|
5
5
|
Blueprint,
|
|
6
6
|
g,
|
|
7
|
-
request
|
|
7
|
+
request, Response
|
|
8
8
|
)
|
|
9
9
|
from flask.json import jsonify
|
|
10
10
|
from argus.backend.error_handlers import handle_api_exception
|
|
@@ -382,18 +382,21 @@ def test_info():
|
|
|
382
382
|
"response": info
|
|
383
383
|
}
|
|
384
384
|
|
|
385
|
-
@bp.route("/test-results", methods=["GET"])
|
|
385
|
+
@bp.route("/test-results", methods=["GET", "HEAD"])
|
|
386
386
|
@api_login_required
|
|
387
387
|
def test_results():
|
|
388
388
|
test_id = request.args.get("testId")
|
|
389
389
|
if not test_id:
|
|
390
390
|
raise Exception("No testId provided")
|
|
391
391
|
service = ResultsService()
|
|
392
|
-
|
|
392
|
+
if request.method == 'HEAD':
|
|
393
|
+
exists = service.is_results_exist(test_id=UUID(test_id))
|
|
394
|
+
return Response(status=200 if exists else 404)
|
|
395
|
+
graphs, ticks = service.get_test_graphs(test_id=UUID(test_id))
|
|
393
396
|
|
|
394
397
|
return {
|
|
395
398
|
"status": "ok",
|
|
396
|
-
"response":
|
|
399
|
+
"response": {"graphs": graphs, "ticks": ticks}
|
|
397
400
|
}
|
|
398
401
|
|
|
399
402
|
@bp.route("/test_run/comment/get", methods=["GET"]) # TODO: remove
|
|
@@ -105,8 +105,4 @@ def run_finalize(run_type: str, run_id: str):
|
|
|
105
105
|
@api_login_required
|
|
106
106
|
def submit_results(run_type: str, run_id: str):
|
|
107
107
|
payload = get_payload(request)
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
"status": "ok",
|
|
111
|
-
"response": result
|
|
112
|
-
}
|
|
108
|
+
return ClientService().submit_results(run_type=run_type, run_id=run_id, results=payload)
|
|
@@ -8,6 +8,7 @@ from flask import (
|
|
|
8
8
|
from argus.backend.error_handlers import handle_api_exception
|
|
9
9
|
from argus.backend.models.web import ArgusTest
|
|
10
10
|
from argus.backend.service.jenkins_service import JenkinsService
|
|
11
|
+
from argus.backend.service.results_service import ResultsService
|
|
11
12
|
from argus.backend.service.testrun import TestRunService
|
|
12
13
|
from argus.backend.service.user import api_login_required
|
|
13
14
|
from argus.backend.util.common import get_payload
|
|
@@ -67,7 +68,7 @@ def test_run_activity(run_id: str):
|
|
|
67
68
|
@bp.route("/run/<string:test_id>/<string:run_id>/fetch_results", methods=["GET"])
|
|
68
69
|
@api_login_required
|
|
69
70
|
def fetch_results(test_id: str, run_id: str):
|
|
70
|
-
tables =
|
|
71
|
+
tables = ResultsService().get_run_results(test_id=UUID(test_id), run_id=UUID(run_id))
|
|
71
72
|
return {
|
|
72
73
|
"status": "ok",
|
|
73
74
|
"tables": tables
|
|
@@ -54,7 +54,7 @@ class ScyllaCluster:
|
|
|
54
54
|
return self.cluster.connect(keyspace=self.config["SCYLLA_KEYSPACE_NAME"])
|
|
55
55
|
|
|
56
56
|
@classmethod
|
|
57
|
-
def get(cls, config:
|
|
57
|
+
def get(cls, config: dict = None) -> 'ScyllaCluster':
|
|
58
58
|
if cls.APP_INSTANCE:
|
|
59
59
|
return cls.APP_INSTANCE
|
|
60
60
|
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import math
|
|
2
|
+
from datetime import datetime, timezone
|
|
3
|
+
|
|
4
|
+
from cassandra.cqlengine import columns
|
|
5
|
+
from cassandra.cqlengine.models import Model
|
|
6
|
+
from cassandra.cqlengine.usertype import UserType
|
|
7
|
+
|
|
8
|
+
class ValidationRules(UserType):
|
|
9
|
+
valid_from = columns.DateTime()
|
|
10
|
+
best_pct = columns.Double() # max value limit relative to best result in percent unit
|
|
11
|
+
best_abs = columns.Double() # max value limit relative to best result in absolute unit
|
|
12
|
+
fixed_limit = columns.Double() # fixed limit
|
|
13
|
+
|
|
14
|
+
class ColumnMetadata(UserType):
|
|
15
|
+
name = columns.Ascii()
|
|
16
|
+
unit = columns.Text()
|
|
17
|
+
type = columns.Ascii()
|
|
18
|
+
higher_is_better = columns.Boolean() # used for tracking best results, if None - no tracking
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ArgusGenericResultMetadata(Model):
|
|
22
|
+
__table_name__ = "generic_result_metadata_v1"
|
|
23
|
+
test_id = columns.UUID(partition_key=True)
|
|
24
|
+
name = columns.Text(required=True, primary_key=True)
|
|
25
|
+
description = columns.Text()
|
|
26
|
+
columns_meta = columns.List(value_type=columns.UserDefinedType(ColumnMetadata))
|
|
27
|
+
validation_rules = columns.Map(key_type=columns.Ascii(), value_type=columns.List(columns.UserDefinedType(ValidationRules)))
|
|
28
|
+
rows_meta = columns.List(value_type=columns.Ascii())
|
|
29
|
+
|
|
30
|
+
def __init__(self, **kwargs):
|
|
31
|
+
kwargs["columns_meta"] = [ColumnMetadata(**col) for col in kwargs.pop('columns_meta', [])]
|
|
32
|
+
validation_rules = kwargs.pop('validation_rules', {})
|
|
33
|
+
|
|
34
|
+
if validation_rules:
|
|
35
|
+
for column, rule in validation_rules.items():
|
|
36
|
+
if not isinstance(rule, list):
|
|
37
|
+
rule['valid_from'] = datetime.now(timezone.utc)
|
|
38
|
+
validation_rules[column] = [rule]
|
|
39
|
+
kwargs["validation_rules"] = {k: [ValidationRules(**rules) for rules in v] for k, v in validation_rules.items()}
|
|
40
|
+
super().__init__(**kwargs)
|
|
41
|
+
|
|
42
|
+
def update_validation_rules(self, key: str, new_rule_dict: dict) -> bool:
|
|
43
|
+
"""
|
|
44
|
+
Checks if the most recent ValidationRule for the given key matches the new_rule_dict.
|
|
45
|
+
If not, adds the new rule to the list with the current timestamp.
|
|
46
|
+
|
|
47
|
+
:param key: The key (column name) in the validation_rules map to update.
|
|
48
|
+
:param new_rule_dict: A dictionary containing the new validation rule values.
|
|
49
|
+
:return: True if a new rule was added, False if the existing rule matches.
|
|
50
|
+
"""
|
|
51
|
+
rules_list = self.validation_rules.get(key, [])
|
|
52
|
+
most_recent_rule = None
|
|
53
|
+
|
|
54
|
+
if rules_list:
|
|
55
|
+
most_recent_rule = rules_list[-1]
|
|
56
|
+
|
|
57
|
+
fields_to_compare = [field for field in ValidationRules._fields if field != 'valid_from']
|
|
58
|
+
rules_match = True
|
|
59
|
+
if most_recent_rule:
|
|
60
|
+
for field in fields_to_compare:
|
|
61
|
+
db_value = getattr(most_recent_rule, field)
|
|
62
|
+
new_value = new_rule_dict.get(field)
|
|
63
|
+
if db_value is None and new_value is None:
|
|
64
|
+
continue
|
|
65
|
+
if db_value is None or new_value is None:
|
|
66
|
+
rules_match = False
|
|
67
|
+
break
|
|
68
|
+
if not math.isclose(db_value, new_value, rel_tol=1e-9, abs_tol=0.0):
|
|
69
|
+
rules_match = False
|
|
70
|
+
break
|
|
71
|
+
else:
|
|
72
|
+
rules_match = False
|
|
73
|
+
|
|
74
|
+
if not rules_match:
|
|
75
|
+
new_rule = ValidationRules(
|
|
76
|
+
valid_from=datetime.now(timezone.utc),
|
|
77
|
+
best_pct=new_rule_dict.get('best_pct'),
|
|
78
|
+
best_abs=new_rule_dict.get('best_abs'),
|
|
79
|
+
fixed_limit=new_rule_dict.get('fixed_limit')
|
|
80
|
+
)
|
|
81
|
+
rules_list.append(new_rule)
|
|
82
|
+
self.validation_rules = self.validation_rules or {}
|
|
83
|
+
self.validation_rules.update({key: rules_list})
|
|
84
|
+
return True
|
|
85
|
+
|
|
86
|
+
return False # Existing rule matches
|
|
87
|
+
|
|
88
|
+
def update_if_changed(self, new_data: dict) -> "ArgusGenericResultMetadata":
|
|
89
|
+
"""
|
|
90
|
+
Updates table metadata if changed column/description or new rows were added.
|
|
91
|
+
See that rows can only be added, not removed once was sent.
|
|
92
|
+
Columns may be removed, but data in results table persists.
|
|
93
|
+
"""
|
|
94
|
+
updated = False
|
|
95
|
+
for field, value in new_data.items():
|
|
96
|
+
if field == "columns_meta":
|
|
97
|
+
value = [ColumnMetadata(**col) for col in value]
|
|
98
|
+
if self.columns_meta != value:
|
|
99
|
+
self.columns_meta = value
|
|
100
|
+
updated = True
|
|
101
|
+
elif field == "rows_meta":
|
|
102
|
+
added_rows = []
|
|
103
|
+
for row in value:
|
|
104
|
+
if row not in self.rows_meta:
|
|
105
|
+
added_rows.append(row)
|
|
106
|
+
updated = True
|
|
107
|
+
self.rows_meta += added_rows
|
|
108
|
+
elif field == "validation_rules":
|
|
109
|
+
if any([self.update_validation_rules(key, rules) for key, rules in value.items()]):
|
|
110
|
+
updated = True
|
|
111
|
+
elif getattr(self, field) != value:
|
|
112
|
+
setattr(self, field, value)
|
|
113
|
+
updated = True
|
|
114
|
+
|
|
115
|
+
if updated:
|
|
116
|
+
self.save()
|
|
117
|
+
return self
|
|
118
|
+
|
|
119
|
+
class ArgusGenericResultData(Model):
|
|
120
|
+
__table_name__ = "generic_result_data_v1"
|
|
121
|
+
test_id = columns.UUID(partition_key=True)
|
|
122
|
+
name = columns.Text(partition_key=True)
|
|
123
|
+
run_id = columns.UUID(primary_key=True)
|
|
124
|
+
column = columns.Ascii(primary_key=True, index=True)
|
|
125
|
+
row = columns.Ascii(primary_key=True, index=True)
|
|
126
|
+
sut_timestamp = columns.DateTime() # for sorting
|
|
127
|
+
value = columns.Double()
|
|
128
|
+
value_text = columns.Text()
|
|
129
|
+
status = columns.Ascii()
|
|
130
|
+
|
|
131
|
+
class ArgusBestResultData(Model):
|
|
132
|
+
__table_name__ = "generic_result_best_v1"
|
|
133
|
+
test_id = columns.UUID(partition_key=True)
|
|
134
|
+
name = columns.Text(partition_key=True)
|
|
135
|
+
key = columns.Ascii(primary_key=True) # represents pair column:row
|
|
136
|
+
result_date = columns.DateTime(primary_key=True, clustering_order="DESC")
|
|
137
|
+
value = columns.Double()
|
|
138
|
+
run_id = columns.UUID()
|
|
@@ -6,7 +6,7 @@ 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
|
|
9
|
+
from argus.backend.models.result import ArgusGenericResultMetadata, ArgusGenericResultData, ArgusBestResultData
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def uuid_now():
|
|
@@ -381,6 +381,7 @@ USED_MODELS: list[Model] = [
|
|
|
381
381
|
ArgusScheduleTest,
|
|
382
382
|
ArgusGenericResultMetadata,
|
|
383
383
|
ArgusGenericResultData,
|
|
384
|
+
ArgusBestResultData,
|
|
384
385
|
]
|
|
385
386
|
|
|
386
387
|
USED_TYPES: list[UserType] = [
|
|
@@ -30,7 +30,7 @@ def plugin_loader() -> dict[str, PluginInfoBase]:
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
AVAILABLE_PLUGINS = plugin_loader()
|
|
33
|
-
|
|
33
|
+
print(AVAILABLE_PLUGINS)
|
|
34
34
|
|
|
35
35
|
def all_plugin_models(include_all=False) -> list[PluginModelBase]:
|
|
36
36
|
return [model for plugin in AVAILABLE_PLUGINS.values() for model in plugin.all_models if issubclass(model, PluginModelBase) or include_all]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
import logging
|
|
3
|
-
from datetime import datetime
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
4
|
from dataclasses import dataclass, field
|
|
5
5
|
from typing import Optional
|
|
6
6
|
from uuid import UUID
|
|
@@ -254,8 +254,15 @@ class SCTTestRun(PluginModelBase):
|
|
|
254
254
|
def sut_timestamp(self) -> float:
|
|
255
255
|
"""converts scylla-server date to timestamp and adds revision in subseconds precision to diffirentiate
|
|
256
256
|
scylla versions from the same day. It's not perfect, but we don't know exact version time."""
|
|
257
|
-
|
|
258
|
-
|
|
257
|
+
try:
|
|
258
|
+
scylla_package_upgraded = [package for package in self.packages if package.name == "scylla-server-upgraded"][0]
|
|
259
|
+
except IndexError:
|
|
260
|
+
scylla_package_upgraded = None
|
|
261
|
+
try:
|
|
262
|
+
scylla_package = [package for package in self.packages if package.name == "scylla-server"][0]
|
|
263
|
+
except IndexError:
|
|
264
|
+
raise ValueError("Scylla package not found in packages - cannot determine SUT timestamp")
|
|
265
|
+
return (datetime.strptime(scylla_package.date, '%Y%m%d').replace(tzinfo=timezone.utc).timestamp()
|
|
259
266
|
+ int(scylla_package.revision_id, 16) % 1000000 / 1000000)
|
|
260
267
|
|
|
261
268
|
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
import operator
|
|
2
|
+
from dataclasses import asdict, is_dataclass
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
from functools import partial
|
|
1
5
|
from uuid import UUID
|
|
6
|
+
|
|
2
7
|
from argus.backend.db import ScyllaCluster
|
|
3
8
|
from argus.backend.models.result import ArgusGenericResultMetadata, ArgusGenericResultData
|
|
4
9
|
from argus.backend.plugins.core import PluginModelBase
|
|
5
10
|
from argus.backend.plugins.loader import AVAILABLE_PLUGINS
|
|
11
|
+
from argus.backend.service.results_service import ResultsService, Cell
|
|
6
12
|
from argus.backend.util.enums import TestStatus
|
|
7
13
|
|
|
8
14
|
|
|
@@ -25,8 +31,9 @@ class ClientService:
|
|
|
25
31
|
def submit_run(self, run_type: str, request_data: dict) -> str:
|
|
26
32
|
model = self.get_model(run_type)
|
|
27
33
|
model.submit_run(request_data=request_data)
|
|
34
|
+
|
|
28
35
|
return "Created"
|
|
29
|
-
|
|
36
|
+
|
|
30
37
|
def get_run(self, run_type: str, run_id: str):
|
|
31
38
|
model = self.get_model(run_type)
|
|
32
39
|
try:
|
|
@@ -79,23 +86,37 @@ class ClientService:
|
|
|
79
86
|
|
|
80
87
|
return "Finalized"
|
|
81
88
|
|
|
82
|
-
def submit_results(self, run_type: str, run_id: str, results: dict) -> str:
|
|
89
|
+
def submit_results(self, run_type: str, run_id: str, results: dict) -> dict[str, str]:
|
|
83
90
|
model = self.get_model(run_type)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
91
|
+
try:
|
|
92
|
+
run = model.load_test_run(UUID(run_id))
|
|
93
|
+
except model.DoesNotExist:
|
|
94
|
+
return {"status": "error", "response": {
|
|
95
|
+
"exception": "DoesNotExist",
|
|
96
|
+
"arguments": [run_id]
|
|
97
|
+
}}
|
|
98
|
+
table_name = results["meta"]["name"]
|
|
99
|
+
results_service = ResultsService()
|
|
100
|
+
cells = [Cell(**cell) for cell in results["results"]]
|
|
101
|
+
table_metadata = results_service.get_table_metadata(test_id=run.test_id, table_name=table_name)
|
|
102
|
+
if table_metadata:
|
|
103
|
+
table_metadata = table_metadata.update_if_changed(results["meta"])
|
|
88
104
|
else:
|
|
89
|
-
ArgusGenericResultMetadata(test_id=run.test_id, **results["meta"])
|
|
105
|
+
table_metadata = ArgusGenericResultMetadata(test_id=run.test_id, **results["meta"])
|
|
106
|
+
table_metadata.save()
|
|
90
107
|
if results.get("sut_timestamp", 0) == 0:
|
|
91
108
|
results["sut_timestamp"] = run.sut_timestamp() # automatic sut_timestamp
|
|
109
|
+
results["sut_timestamp"] = datetime.fromtimestamp(results["sut_timestamp"])
|
|
110
|
+
best_results = results_service.update_best_results(test_id=run.test_id, table_name=table_name, table_metadata=table_metadata,
|
|
111
|
+
cells=cells, run_id=run_id)
|
|
92
112
|
table_name = results["meta"]["name"]
|
|
93
113
|
sut_timestamp = results["sut_timestamp"]
|
|
94
|
-
for cell in
|
|
114
|
+
for cell in cells:
|
|
115
|
+
cell.update_cell_status_based_on_rules(table_metadata, best_results)
|
|
95
116
|
ArgusGenericResultData(test_id=run.test_id,
|
|
96
117
|
run_id=run.id,
|
|
97
118
|
name=table_name,
|
|
98
119
|
sut_timestamp=sut_timestamp,
|
|
99
|
-
**cell
|
|
120
|
+
**asdict(cell)
|
|
100
121
|
).save()
|
|
101
|
-
return "
|
|
122
|
+
return {"status": "ok", "message": "Results submitted"}
|
|
@@ -93,7 +93,8 @@ class NotificationSenderBase:
|
|
|
93
93
|
|
|
94
94
|
class ArgusDBNotificationSaver(NotificationSenderBase):
|
|
95
95
|
CONTENT_TEMPLATES = {
|
|
96
|
-
ArgusNotificationTypes.Mention: lambda p: render_template("notifications/mention.html.j2", **p if p else {})
|
|
96
|
+
ArgusNotificationTypes.Mention: lambda p: render_template("notifications/mention.html.j2", **p if p else {}),
|
|
97
|
+
ArgusNotificationTypes.AssigneeChange: lambda p: render_template("notifications/assigned.html.j2", **p if p else {}),
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
def send_notification(self, receiver: UUID, sender: UUID, notification_type: ArgusNotificationTypes, source_type: ArgusNotificationSourceTypes,
|
|
@@ -117,7 +118,8 @@ class ArgusDBNotificationSaver(NotificationSenderBase):
|
|
|
117
118
|
class EmailNotificationServiceSender(NotificationSenderBase):
|
|
118
119
|
CONTENT_TEMPLATES = {
|
|
119
120
|
ArgusNotificationTypes.Mention: lambda p: render_template(
|
|
120
|
-
"notifications/email_mention.html.j2", **p if p else {})
|
|
121
|
+
"notifications/email_mention.html.j2", **p if p else {}),
|
|
122
|
+
ArgusNotificationTypes.AssigneeChange: lambda p: render_template("notifications/assigned_email.html.j2", **p if p else {}),
|
|
121
123
|
}
|
|
122
124
|
|
|
123
125
|
def __init__(self):
|