argus-alm 0.12.3__tar.gz → 0.12.4b2__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 (91) hide show
  1. argus_alm-0.12.4b2/PKG-INFO +129 -0
  2. argus_alm-0.12.4b2/README.md +109 -0
  3. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/controller/admin_api.py +26 -0
  4. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/controller/api.py +26 -1
  5. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/controller/main.py +21 -0
  6. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/controller/testrun_api.py +16 -0
  7. argus_alm-0.12.4b2/argus/backend/controller/view_api.py +162 -0
  8. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/models/web.py +16 -0
  9. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/plugins/core.py +25 -10
  10. argus_alm-0.12.4b2/argus/backend/plugins/driver_matrix_tests/controller.py +63 -0
  11. argus_alm-0.12.4b2/argus/backend/plugins/driver_matrix_tests/model.py +421 -0
  12. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/plugins/driver_matrix_tests/raw_types.py +27 -0
  13. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/plugins/driver_matrix_tests/service.py +18 -0
  14. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/plugins/driver_matrix_tests/udt.py +14 -13
  15. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/plugins/generic/model.py +5 -2
  16. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/plugins/sct/service.py +13 -1
  17. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/service/argus_service.py +116 -20
  18. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/service/build_system_monitor.py +37 -7
  19. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/service/jenkins_service.py +2 -1
  20. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/service/release_manager.py +14 -0
  21. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/service/stats.py +147 -11
  22. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/service/testrun.py +44 -5
  23. argus_alm-0.12.4b2/argus/backend/service/views.py +258 -0
  24. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/template_filters.py +7 -0
  25. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/util/common.py +14 -2
  26. argus_alm-0.12.4b2/argus/client/driver_matrix_tests/cli.py +110 -0
  27. argus_alm-0.12.4b2/argus/client/driver_matrix_tests/client.py +79 -0
  28. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/pyproject.toml +2 -1
  29. argus_alm-0.12.3/PKG-INFO +0 -207
  30. argus_alm-0.12.3/README.md +0 -187
  31. argus_alm-0.12.3/argus/backend/plugins/driver_matrix_tests/controller.py +0 -24
  32. argus_alm-0.12.3/argus/backend/plugins/driver_matrix_tests/model.py +0 -173
  33. argus_alm-0.12.3/argus/client/driver_matrix_tests/client.py +0 -216
  34. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/LICENSE +0 -0
  35. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/__init__.py +0 -0
  36. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/.gitkeep +0 -0
  37. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/__init__.py +0 -0
  38. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/cli.py +0 -0
  39. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/controller/__init__.py +0 -0
  40. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/controller/admin.py +0 -0
  41. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/controller/auth.py +0 -0
  42. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/controller/client_api.py +0 -0
  43. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/controller/notification_api.py +0 -0
  44. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/controller/notifications.py +0 -0
  45. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/controller/team.py +0 -0
  46. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/controller/team_ui.py +0 -0
  47. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/db.py +0 -0
  48. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/error_handlers.py +0 -0
  49. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/events/event_processors.py +0 -0
  50. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/models/__init__.py +0 -0
  51. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/plugins/__init__.py +0 -0
  52. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/plugins/driver_matrix_tests/plugin.py +0 -0
  53. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/plugins/generic/plugin.py +0 -0
  54. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/plugins/generic/types.py +0 -0
  55. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/plugins/loader.py +0 -0
  56. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/plugins/sct/controller.py +0 -0
  57. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/plugins/sct/plugin.py +0 -0
  58. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/plugins/sct/resource_setup.py +0 -0
  59. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/plugins/sct/testrun.py +0 -0
  60. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/plugins/sct/types.py +0 -0
  61. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/plugins/sct/udt.py +0 -0
  62. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/plugins/sirenada/model.py +0 -0
  63. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/plugins/sirenada/plugin.py +0 -0
  64. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/plugins/sirenada/types.py +0 -0
  65. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/service/admin.py +0 -0
  66. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/service/client_service.py +0 -0
  67. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/service/event_service.py +0 -0
  68. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/service/notification_manager.py +0 -0
  69. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/service/team_manager_service.py +0 -0
  70. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/service/user.py +0 -0
  71. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/util/config.py +0 -0
  72. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/util/encoders.py +0 -0
  73. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/util/enums.py +0 -0
  74. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/util/logsetup.py +0 -0
  75. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/util/module_loaders.py +0 -0
  76. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/backend/util/send_email.py +0 -0
  77. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/client/__init__.py +0 -0
  78. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/client/base.py +0 -0
  79. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/client/generic/cli.py +0 -0
  80. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/client/generic/client.py +0 -0
  81. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/client/sct/client.py +0 -0
  82. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/client/sct/types.py +0 -0
  83. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/client/sirenada/client.py +0 -0
  84. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/db/.gitkeep +0 -0
  85. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/db/argus_json.py +0 -0
  86. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/db/cloud_types.py +0 -0
  87. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/db/config.py +0 -0
  88. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/db/db_types.py +0 -0
  89. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/db/interface.py +0 -0
  90. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/db/testrun.py +0 -0
  91. {argus_alm-0.12.3 → argus_alm-0.12.4b2}/argus/db/utils.py +0 -0
@@ -0,0 +1,129 @@
1
+ Metadata-Version: 2.1
2
+ Name: argus-alm
3
+ Version: 0.12.4b2
4
+ Summary: Argus
5
+ Home-page: https://github.com/scylladb/argus
6
+ License: Apache-2.0
7
+ Author: Alexey Kartashov
8
+ Author-email: alexey.kartashov@scylladb.com
9
+ Requires-Python: >=3.10,<4.0
10
+ Classifier: License :: OSI Approved :: Apache Software License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Dist: click (>=8.1.3,<9.0.0)
16
+ Requires-Dist: requests (>=2.26.0,<3.0.0)
17
+ Project-URL: Repository, https://github.com/scylladb/argus
18
+ Description-Content-Type: text/markdown
19
+
20
+ # Argus
21
+
22
+ ## Description
23
+
24
+ Argus is a test tracking system intended to provide observability into automated test pipelines which use long-running resources. It allows observation of a test status, its events and its allocated resources. It also allows easy comparison between particular runs of a specific test.
25
+
26
+ ## Installation notes
27
+
28
+ ### Development
29
+
30
+ For development setup instructions, see [dev-setup.md](./docs/dev-setup.md).
31
+
32
+ ### Prerequisites
33
+
34
+ - Python >=3.10.0 (system-wide or pyenv)
35
+
36
+ - NodeJS >=16 (with npm)
37
+
38
+ - Yarn (can be installed globally with `npm -g install yarn`)
39
+
40
+ - nginx
41
+
42
+ - poetry >=1.2.0b1
43
+
44
+ ### From source
45
+
46
+ #### Production
47
+
48
+ Perform the following steps:
49
+
50
+ Create a user that will be used by uwsgi:
51
+
52
+ ```bash
53
+ useradd -m -s /bin/bash argus
54
+ sudo -iu argus
55
+ ```
56
+
57
+ (Optional) Install pyenv and create a virtualenv for this user:
58
+
59
+ ```bash
60
+ pyenv install 3.10.0
61
+ pyenv virtualenv argus
62
+ pyenv activate argus
63
+ ```
64
+
65
+ Clone the project into a directory somewhere where user has full write permissions
66
+
67
+ ```bash
68
+ git clone https://github.com/scylladb/argus ~/app
69
+ cd ~/app
70
+ ```
71
+
72
+ Install project dependencies:
73
+
74
+ ```bash
75
+ poetry install --with default,dev,web-backend,docker-image
76
+ yarn install
77
+ ```
78
+
79
+ Compile frontend files from `/frontend` into `/public/dist`
80
+
81
+ ```bash
82
+ yarn webpack
83
+ ```
84
+
85
+ Create a `argus.local.yaml` configuration file (used to configure database connection) and a `argus_web.yaml` (used for webapp secrets) in your application install directory.
86
+
87
+ ```bash
88
+ cp argus_web.example.yaml argus_web.yaml
89
+ cp argus.yaml argus.local.yaml
90
+ ```
91
+
92
+ Open `argus.local.yaml` and add the database connection information (contact_points, user, password and keyspace name).
93
+
94
+ Open `argus_web.yaml` and change the `SECRET_KEY` value to something secure, like a sha512 digest of random bytes. Fill out GITHUB_* variables with their respective values.
95
+
96
+ Copy nginx configuration file from `docs/configs/argus.nginx.conf` to nginx virtual hosts directory:
97
+
98
+ Ubuntu:
99
+
100
+ ```bash
101
+ sudo cp docs/configs/argus.nginx.conf /etc/nginx/sites-available/argus
102
+ sudo ln -s /etc/nginx/sites-enabled/argus /etc/nginx/sites-available/argus
103
+ ```
104
+
105
+ RHEL/Centos/Alma/Fedora:
106
+
107
+ ```bash
108
+ sudo cp docs/configs/argus.nginx.conf /etc/nginx/conf.d/argus.conf
109
+ ```
110
+
111
+ Adjust the webhost settings in that file as necessary, particularly `listen` and `server_name` directives.
112
+
113
+ Copy systemd service file from `docs/config/argus.service` to `/etc/systemd/system` directory:
114
+
115
+ ```bash
116
+ sudo cp docs/config/argus.service /etc/systemd/system
117
+ ```
118
+
119
+ Open it and adjust the path to the `start_argus.sh` script in the `ExecStart=` directive and the user/group, then reload systemd daemon configuration and enable (and optionally start) the service.
120
+
121
+ WARNING: `start_argus.sh` assumes pyenv is installed into `~/.pyenv`
122
+
123
+ ```bash
124
+ sudo systemctl daemon-reload
125
+ sudo systemctl enable --now argus.service
126
+ ```
127
+
128
+
129
+
@@ -0,0 +1,109 @@
1
+ # Argus
2
+
3
+ ## Description
4
+
5
+ Argus is a test tracking system intended to provide observability into automated test pipelines which use long-running resources. It allows observation of a test status, its events and its allocated resources. It also allows easy comparison between particular runs of a specific test.
6
+
7
+ ## Installation notes
8
+
9
+ ### Development
10
+
11
+ For development setup instructions, see [dev-setup.md](./docs/dev-setup.md).
12
+
13
+ ### Prerequisites
14
+
15
+ - Python >=3.10.0 (system-wide or pyenv)
16
+
17
+ - NodeJS >=16 (with npm)
18
+
19
+ - Yarn (can be installed globally with `npm -g install yarn`)
20
+
21
+ - nginx
22
+
23
+ - poetry >=1.2.0b1
24
+
25
+ ### From source
26
+
27
+ #### Production
28
+
29
+ Perform the following steps:
30
+
31
+ Create a user that will be used by uwsgi:
32
+
33
+ ```bash
34
+ useradd -m -s /bin/bash argus
35
+ sudo -iu argus
36
+ ```
37
+
38
+ (Optional) Install pyenv and create a virtualenv for this user:
39
+
40
+ ```bash
41
+ pyenv install 3.10.0
42
+ pyenv virtualenv argus
43
+ pyenv activate argus
44
+ ```
45
+
46
+ Clone the project into a directory somewhere where user has full write permissions
47
+
48
+ ```bash
49
+ git clone https://github.com/scylladb/argus ~/app
50
+ cd ~/app
51
+ ```
52
+
53
+ Install project dependencies:
54
+
55
+ ```bash
56
+ poetry install --with default,dev,web-backend,docker-image
57
+ yarn install
58
+ ```
59
+
60
+ Compile frontend files from `/frontend` into `/public/dist`
61
+
62
+ ```bash
63
+ yarn webpack
64
+ ```
65
+
66
+ Create a `argus.local.yaml` configuration file (used to configure database connection) and a `argus_web.yaml` (used for webapp secrets) in your application install directory.
67
+
68
+ ```bash
69
+ cp argus_web.example.yaml argus_web.yaml
70
+ cp argus.yaml argus.local.yaml
71
+ ```
72
+
73
+ Open `argus.local.yaml` and add the database connection information (contact_points, user, password and keyspace name).
74
+
75
+ Open `argus_web.yaml` and change the `SECRET_KEY` value to something secure, like a sha512 digest of random bytes. Fill out GITHUB_* variables with their respective values.
76
+
77
+ Copy nginx configuration file from `docs/configs/argus.nginx.conf` to nginx virtual hosts directory:
78
+
79
+ Ubuntu:
80
+
81
+ ```bash
82
+ sudo cp docs/configs/argus.nginx.conf /etc/nginx/sites-available/argus
83
+ sudo ln -s /etc/nginx/sites-enabled/argus /etc/nginx/sites-available/argus
84
+ ```
85
+
86
+ RHEL/Centos/Alma/Fedora:
87
+
88
+ ```bash
89
+ sudo cp docs/configs/argus.nginx.conf /etc/nginx/conf.d/argus.conf
90
+ ```
91
+
92
+ Adjust the webhost settings in that file as necessary, particularly `listen` and `server_name` directives.
93
+
94
+ Copy systemd service file from `docs/config/argus.service` to `/etc/systemd/system` directory:
95
+
96
+ ```bash
97
+ sudo cp docs/config/argus.service /etc/systemd/system
98
+ ```
99
+
100
+ Open it and adjust the path to the `start_argus.sh` script in the `ExecStart=` directive and the user/group, then reload systemd daemon configuration and enable (and optionally start) the service.
101
+
102
+ WARNING: `start_argus.sh` assumes pyenv is installed into `~/.pyenv`
103
+
104
+ ```bash
105
+ sudo systemctl daemon-reload
106
+ sudo systemctl enable --now argus.service
107
+ ```
108
+
109
+
@@ -261,3 +261,29 @@ def get_tests_for_group():
261
261
  "status": "ok",
262
262
  "response": tests
263
263
  }
264
+
265
+
266
+ @bp.route("/release/test/state/toggle", methods=["POST"])
267
+ @check_roles(UserRoles.Admin)
268
+ @api_login_required
269
+ def quick_toggle_test_enabled():
270
+
271
+ payload = get_payload(request)
272
+ res = ReleaseManagerService().toggle_test_enabled(test_id=payload["entityId"], new_state=payload["state"])
273
+ return {
274
+ "status": "ok",
275
+ "response": res
276
+ }
277
+
278
+
279
+ @bp.route("/release/group/state/toggle", methods=["POST"])
280
+ @check_roles(UserRoles.Admin)
281
+ @api_login_required
282
+ def quick_toggle_group_enabled():
283
+
284
+ payload = get_payload(request)
285
+ res = ReleaseManagerService().toggle_group_enabled(group_id=payload["entityId"], new_state=payload["state"])
286
+ return {
287
+ "status": "ok",
288
+ "response": res
289
+ }
@@ -12,7 +12,8 @@ from argus.backend.controller.notification_api import bp as notifications_bp
12
12
  from argus.backend.controller.client_api import bp as client_bp
13
13
  from argus.backend.controller.testrun_api import bp as testrun_bp
14
14
  from argus.backend.controller.team import bp as team_bp
15
- from argus.backend.service.argus_service import ArgusService
15
+ from argus.backend.controller.view_api import bp as view_bp
16
+ from argus.backend.service.argus_service import ArgusService, ScheduleUpdateRequest
16
17
  from argus.backend.service.user import UserService, api_login_required
17
18
  from argus.backend.service.stats import ReleaseStatsCollector
18
19
  from argus.backend.models.web import ArgusRelease, ArgusGroup, ArgusTest, User, UserOauthToken
@@ -23,6 +24,7 @@ bp.register_blueprint(notifications_bp)
23
24
  bp.register_blueprint(client_bp)
24
25
  bp.register_blueprint(testrun_bp)
25
26
  bp.register_blueprint(team_bp)
27
+ bp.register_blueprint(view_bp)
26
28
  bp.register_error_handler(Exception, handle_api_exception)
27
29
  LOGGER = logging.getLogger(__name__)
28
30
 
@@ -227,6 +229,8 @@ def release_schedules_submit():
227
229
  groups=payload["groups"],
228
230
  assignees=payload["assignees"],
229
231
  tag=payload["tag"],
232
+ comments=payload.get("comments"),
233
+ group_ids=payload.get("groupIds"),
230
234
  )
231
235
 
232
236
  return jsonify({
@@ -251,6 +255,27 @@ def release_schedules_delete():
251
255
  })
252
256
 
253
257
 
258
+ @bp.route("/release/schedules/update", methods=["POST"])
259
+ @api_login_required
260
+ def release_schedule_update():
261
+ payload = get_payload(request)
262
+ req = ScheduleUpdateRequest(**payload)
263
+ service = ArgusService()
264
+ update_result = service.update_schedule(
265
+ release_id=req.release_id,
266
+ schedule_id=req.schedule_id,
267
+ old_tests=req.old_tests,
268
+ new_tests=req.new_tests,
269
+ comments=req.comments,
270
+ assignee=req.assignee
271
+ )
272
+
273
+ return jsonify({
274
+ "status": "ok",
275
+ "response": update_result
276
+ })
277
+
278
+
254
279
  @bp.route("/groups", methods=["GET"])
255
280
  @api_login_required
256
281
  def argus_groups():
@@ -1,3 +1,5 @@
1
+ import datetime
2
+ import json
1
3
  import logging
2
4
  from uuid import UUID
3
5
  from flask import (
@@ -8,6 +10,7 @@ from argus.backend.controller.team_ui import bp as teams_bp
8
10
  from argus.backend.service.argus_service import ArgusService
9
11
  from argus.backend.models.web import WebFileStorage
10
12
  from argus.backend.service.user import UserService, login_required
13
+ from argus.backend.service.views import UserViewService
11
14
 
12
15
  LOGGER = logging.getLogger(__name__)
13
16
 
@@ -55,6 +58,24 @@ def releases():
55
58
  return render_template("releases.html.j2", releases=all_releases)
56
59
 
57
60
 
61
+ @bp.route("/views")
62
+ @login_required
63
+ def views():
64
+ service = UserViewService()
65
+ all_views = service.get_all_views()
66
+ return render_template("views.html.j2", views=sorted(all_views, key=lambda view: view.created or datetime.datetime.fromtimestamp(0), reverse=True))
67
+
68
+
69
+ @bp.route("/view/<string:view_name>")
70
+ @login_required
71
+ def view_dashboard(view_name: str):
72
+ service = UserViewService()
73
+ view = service.get_view_by_name(view_name=view_name)
74
+ data_json = view
75
+ view["widget_settings"] = json.loads(view["widget_settings"])
76
+ return render_template("view_dashboard.html.j2", data=data_json)
77
+
78
+
58
79
  @bp.route("/alert_debug")
59
80
  @login_required
60
81
  def alert_debug():
@@ -289,6 +289,22 @@ def ignore_jobs():
289
289
  }
290
290
 
291
291
 
292
+ @bp.route("/get_runs_by_test_id_run_id", methods=["POST"])
293
+ @api_login_required
294
+ def get_runs_by_test_id_run_id():
295
+ payload: list[tuple[UUID, UUID]] = get_payload(request)
296
+ service = TestRunService()
297
+
298
+ result = service.resolve_run_build_id_and_number_multiple(payload)
299
+
300
+ return {
301
+ "status": "ok",
302
+ "response": {
303
+ "runs": result
304
+ }
305
+ }
306
+
307
+
292
308
  @bp.route("/jenkins/params", methods=["POST"])
293
309
  @api_login_required
294
310
  def get_jenkins_job_params():
@@ -0,0 +1,162 @@
1
+ import logging
2
+ from uuid import UUID
3
+ from flask import (
4
+ Blueprint,
5
+ jsonify,
6
+ request,
7
+ )
8
+ from argus.backend.error_handlers import handle_api_exception
9
+ from argus.backend.models.web import User
10
+ from argus.backend.service.stats import ViewStatsCollector
11
+ from argus.backend.service.user import api_login_required
12
+ from argus.backend.service.views import UserViewService
13
+ from argus.backend.util.common import get_payload
14
+
15
+ bp = Blueprint('view_api', __name__, url_prefix='/views')
16
+ LOGGER = logging.getLogger(__name__)
17
+ bp.register_error_handler(Exception, handle_api_exception)
18
+
19
+
20
+ class ViewApiException(Exception):
21
+ pass
22
+
23
+
24
+ @bp.route("/", methods=["GET"])
25
+ @api_login_required
26
+ def index():
27
+ return {
28
+ "status": "ok",
29
+ "response": {
30
+ "version": "v1",
31
+ }
32
+ }
33
+
34
+
35
+ @bp.route("/create", methods=["POST"])
36
+ @api_login_required
37
+ def create_view():
38
+ payload = get_payload(request)
39
+ service = UserViewService()
40
+ view = service.create_view(
41
+ name=payload["name"],
42
+ items=payload["items"],
43
+ widget_settings=payload["settings"],
44
+ description=payload.get("description"),
45
+ display_name=payload.get("displayName")
46
+ )
47
+ return {
48
+ "status": "ok",
49
+ "response": view
50
+ }
51
+
52
+
53
+ @bp.route("/get", methods=["GET"])
54
+ @api_login_required
55
+ def get_view():
56
+ view_id = request.args.get("viewId")
57
+ if not view_id:
58
+ raise ViewApiException("No viewId provided.")
59
+ service = UserViewService()
60
+ view = service.get_view(UUID(view_id))
61
+ return {
62
+ "status": "ok",
63
+ "response": view
64
+ }
65
+
66
+
67
+ @bp.route("/all", methods=["GET"])
68
+ @api_login_required
69
+ def get_all_views():
70
+ user_id = request.args.get("userId")
71
+ if user_id:
72
+ user = User.get(id=user_id)
73
+ else:
74
+ user = None
75
+ service = UserViewService()
76
+ views = service.get_all_views(user)
77
+ return {
78
+ "status": "ok",
79
+ "response": views
80
+ }
81
+
82
+
83
+ @bp.route("/update", methods=["POST"])
84
+ @api_login_required
85
+ def update_view():
86
+ payload = get_payload(request)
87
+ service = UserViewService()
88
+ res = service.update_view(view_id=payload["viewId"], update_data=payload["updateData"])
89
+ return {
90
+ "status": "ok",
91
+ "response": res
92
+ }
93
+
94
+
95
+ @bp.route("/delete", methods=["POST"])
96
+ @api_login_required
97
+ def delete_view():
98
+ payload = get_payload(request)
99
+ service = UserViewService()
100
+ res = service.delete_view(payload["viewId"])
101
+ return {
102
+ "status": "ok",
103
+ "response": res
104
+ }
105
+
106
+
107
+ @bp.route("/search", methods=["GET"])
108
+ @api_login_required
109
+ def search_tests():
110
+ query = request.args.get("query")
111
+ service = UserViewService()
112
+ if query:
113
+ res = service.test_lookup(query)
114
+ else:
115
+ res = []
116
+ return {
117
+ "status": "ok",
118
+ "response": {
119
+ "hits": res,
120
+ "total": len(res)
121
+ }
122
+ }
123
+
124
+ @bp.route("/stats", methods=["GET"])
125
+ @api_login_required
126
+ def view_stats():
127
+ view_id = request.args.get("viewId")
128
+ if not view_id:
129
+ raise ViewApiException("No view id provided.")
130
+ limited = bool(int(request.args.get("limited", 0)))
131
+ version = request.args.get("productVersion", None)
132
+ include_no_version = bool(int(request.args.get("includeNoVersion", True)))
133
+ force = bool(int(request.args.get("force", 0)))
134
+ collector = ViewStatsCollector(view_id=view_id, filter=version)
135
+ stats = collector.collect(limited=limited, force=force, include_no_version=include_no_version)
136
+
137
+ res = jsonify({
138
+ "status": "ok",
139
+ "response": stats
140
+ })
141
+ res.cache_control.max_age = 300
142
+ return res
143
+
144
+ @bp.route("/<string:view_id>/versions", methods=["GET"])
145
+ @api_login_required
146
+ def view_versions(view_id: str):
147
+ service = UserViewService()
148
+ res = service.get_versions_for_view(view_id)
149
+ return {
150
+ "status": "ok",
151
+ "response": res
152
+ }
153
+
154
+ @bp.route("/<string:view_id>/resolve", methods=["GET"])
155
+ @api_login_required
156
+ def view_resolve(view_id: str):
157
+ service = UserViewService()
158
+ res = service.resolve_view_for_edit(view_id)
159
+ return {
160
+ "status": "ok",
161
+ "response": res
162
+ }
@@ -146,6 +146,20 @@ class ArgusGroup(Model):
146
146
  return super().__eq__(other)
147
147
 
148
148
 
149
+ class ArgusUserView(Model):
150
+ id = columns.UUID(primary_key=True, partition_key=True, default=uuid4)
151
+ name = columns.Text(required=True, index=True)
152
+ display_name = columns.Text()
153
+ description = columns.Text()
154
+ user_id = columns.UUID(required=True, index=True)
155
+ tests = columns.List(value_type=columns.UUID, default=lambda: [])
156
+ release_ids = columns.List(value_type=columns.UUID, default=lambda: [])
157
+ group_ids = columns.List(value_type=columns.UUID, default=lambda: [])
158
+ created = columns.DateTime(default=datetime.utcnow)
159
+ last_updated = columns.DateTime(default=datetime.utcnow)
160
+ widget_settings = columns.Text(required=True)
161
+
162
+
149
163
  class ArgusTest(Model):
150
164
  __table_name__ = "argus_test_v2"
151
165
  id = columns.UUID(primary_key=True, default=uuid4)
@@ -180,6 +194,7 @@ class ArgusTestRunComment(Model):
180
194
  test_run_id = columns.UUID(required=True, index=True)
181
195
  user_id = columns.UUID(required=True, index=True)
182
196
  release_id = columns.UUID(required=True, index=True)
197
+ test_id = columns.UUID(required=True, index=True)
183
198
  posted_at = columns.Integer(
184
199
  required=True, clustering_order="desc", primary_key=True)
185
200
  message = columns.Text(min_length=1, max_length=65535)
@@ -350,6 +365,7 @@ USED_MODELS: list[Model] = [
350
365
  Team,
351
366
  WebFileStorage,
352
367
  ArgusRelease,
368
+ ArgusUserView,
353
369
  ArgusGroup,
354
370
  ArgusTest,
355
371
  ArgusTestRunComment,
@@ -1,5 +1,6 @@
1
1
  import logging
2
2
  from datetime import datetime
3
+ from math import ceil
3
4
  from uuid import UUID
4
5
  from time import time
5
6
 
@@ -16,8 +17,10 @@ from argus.backend.models.web import (
16
17
  ArgusSchedule,
17
18
  ArgusScheduleTest,
18
19
  ArgusScheduleAssignee,
20
+ ArgusUserView,
19
21
  User
20
22
  )
23
+ from argus.backend.util.common import chunk
21
24
  from argus.backend.util.enums import TestInvestigationStatus, TestStatus
22
25
 
23
26
  LOGGER = logging.getLogger(__name__)
@@ -27,7 +30,7 @@ class PluginModelBase(Model):
27
30
  _plugin_name = "unknown"
28
31
  # Metadata
29
32
  build_id = columns.Text(required=True, partition_key=True)
30
- start_time = columns.DateTime(required=True, primary_key=True, clustering_order="DESC", default=datetime.now, custom_index=True)
33
+ start_time = columns.DateTime(required=True, primary_key=True, clustering_order="DESC", default=datetime.utcnow, custom_index=True)
31
34
  id = columns.UUID(index=True, required=True)
32
35
  release_id = columns.UUID(index=True)
33
36
  group_id = columns.UUID(index=True)
@@ -136,17 +139,14 @@ class PluginModelBase(Model):
136
139
  cluster = ScyllaCluster.get()
137
140
  query = cluster.prepare(cls._stats_query())
138
141
  futures = []
139
- step = 0
140
142
  step_size = 90
141
- total_tests = len(build_ids)
142
- while total_tests > 0:
143
- id_slice = build_ids[step:step+step_size]
144
- futures.append(cluster.session.execute_async(query=query, parameters=(id_slice,)))
145
- step += step_size
146
- total_tests = max(0, total_tests - step_size)
147
143
 
148
- return futures
144
+ for step in range(0, ceil(len(build_ids) / step_size)):
145
+ start_pos = step*step_size
146
+ next_slice = build_ids[start_pos:start_pos+step_size]
147
+ futures.append(cluster.session.execute_async(query=query, parameters=(next_slice,)))
149
148
 
149
+ return futures
150
150
  @classmethod
151
151
  def get_run_meta_by_build_id(cls, build_id: str, limit: int = 10):
152
152
  cluster = ScyllaCluster.get()
@@ -177,6 +177,21 @@ class PluginModelBase(Model):
177
177
  def get_distinct_product_versions(cls, release: ArgusRelease) -> list[str]:
178
178
  raise NotImplementedError()
179
179
 
180
+ @classmethod
181
+ def get_distinct_versions_for_view(cls, tests: list[ArgusTest]) -> list[str]:
182
+ cluster = ScyllaCluster.get()
183
+ statement = cluster.prepare(f"SELECT scylla_version FROM {cls.table_name()} WHERE build_id IN ?")
184
+ futures = []
185
+ for batch in chunk(tests):
186
+ futures.append(cluster.session.execute_async(query=statement, parameters=([t.build_system_id for t in batch],)))
187
+
188
+ rows = []
189
+ for future in futures:
190
+ rows.extend(future.result())
191
+ unique_versions = {r["scylla_version"] for r in rows if r["scylla_version"]}
192
+
193
+ return sorted(list(unique_versions), reverse=True)
194
+
180
195
  def update_heartbeat(self):
181
196
  self.heartbeat = int(time())
182
197
 
@@ -189,7 +204,7 @@ class PluginModelBase(Model):
189
204
  def submit_product_version(self, version: str):
190
205
  raise NotImplementedError()
191
206
 
192
- def set_product_version(self, version: str):
207
+ def set_full_version(self, version: str):
193
208
  self.product_version = version
194
209
 
195
210
  def submit_logs(self, logs: list[dict]):