watcher-tempest-plugin 3.6.0__tar.gz → 3.8.0__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 (103) hide show
  1. watcher_tempest_plugin-3.8.0/.zuul.yaml +66 -0
  2. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/AUTHORS +2 -0
  3. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/ChangeLog +27 -0
  4. {watcher_tempest_plugin-3.6.0/watcher_tempest_plugin.egg-info → watcher_tempest_plugin-3.8.0}/PKG-INFO +17 -4
  5. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/README.rst +4 -0
  6. watcher_tempest_plugin-3.8.0/releasenotes/notes/parametrize-promtool-write-url-path-3534f2b9.yaml +8 -0
  7. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/setup.cfg +2 -2
  8. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/config.py +18 -0
  9. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/infra_optim_clients.py +1 -0
  10. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/services/metric/prometheus_client.py +43 -8
  11. watcher_tempest_plugin-3.8.0/watcher_tempest_plugin/tests/api/admin/base.py +114 -0
  12. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/api/admin/test_action.py +49 -23
  13. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/api/admin/test_action_plan.py +25 -10
  14. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/api/admin/test_audit.py +254 -78
  15. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/api/admin/test_audit_template.py +6 -7
  16. watcher_tempest_plugin-3.8.0/watcher_tempest_plugin/tests/common/base.py +375 -0
  17. watcher_tempest_plugin-3.8.0/watcher_tempest_plugin/tests/scenario/__init__.py +0 -0
  18. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/scenario/base.py +9 -92
  19. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/scenario/test_continuous_audit.py +11 -1
  20. watcher_tempest_plugin-3.8.0/watcher_tempest_plugin/tests/scenario/test_execute_zone_migration.py +464 -0
  21. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0/watcher_tempest_plugin.egg-info}/PKG-INFO +17 -4
  22. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin.egg-info/SOURCES.txt +3 -0
  23. watcher_tempest_plugin-3.8.0/watcher_tempest_plugin.egg-info/pbr.json +1 -0
  24. watcher_tempest_plugin-3.6.0/.zuul.yaml +0 -49
  25. watcher_tempest_plugin-3.6.0/watcher_tempest_plugin/tests/api/admin/base.py +0 -378
  26. watcher_tempest_plugin-3.6.0/watcher_tempest_plugin/tests/scenario/test_execute_zone_migration.py +0 -197
  27. watcher_tempest_plugin-3.6.0/watcher_tempest_plugin.egg-info/pbr.json +0 -1
  28. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/.coveragerc +0 -0
  29. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/.mailmap +0 -0
  30. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/.stestr.conf +0 -0
  31. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/.testr.conf +0 -0
  32. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/CONTRIBUTING.rst +0 -0
  33. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/HACKING.rst +0 -0
  34. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/LICENSE +0 -0
  35. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/doc/requirements.txt +0 -0
  36. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/doc/source/conf.py +0 -0
  37. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/doc/source/contributing.rst +0 -0
  38. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/doc/source/index.rst +0 -0
  39. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/doc/source/readme.rst +0 -0
  40. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/pyproject.toml +0 -0
  41. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/releasenotes/notes/.placeholder +0 -0
  42. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/releasenotes/notes/drop-py-2-7-eb1979587d237f5e.yaml +0 -0
  43. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/releasenotes/notes/drop-python38-support-eeb09a8bc0170ce2.yaml +0 -0
  44. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/releasenotes/source/_static/.placeholder +0 -0
  45. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/releasenotes/source/_templates/.placeholder +0 -0
  46. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/releasenotes/source/conf.py +0 -0
  47. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/releasenotes/source/index.rst +0 -0
  48. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/releasenotes/source/unreleased.rst +0 -0
  49. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/requirements.txt +0 -0
  50. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/setup.py +0 -0
  51. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/test-requirements.txt +0 -0
  52. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/tox.ini +0 -0
  53. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/__init__.py +0 -0
  54. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/plugin.py +0 -0
  55. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/services/__init__.py +0 -0
  56. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/services/base.py +0 -0
  57. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/services/infra_optim/__init__.py +0 -0
  58. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/services/infra_optim/v1/__init__.py +0 -0
  59. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/services/infra_optim/v1/json/__init__.py +0 -0
  60. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/services/infra_optim/v1/json/api_microversion_fixture.py +0 -0
  61. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/services/infra_optim/v1/json/client.py +0 -0
  62. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/services/metric/__init__.py +0 -0
  63. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/services/metric/v1/__init__.py +0 -0
  64. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/services/metric/v1/json/__init__.py +0 -0
  65. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/services/metric/v1/json/client.py +0 -0
  66. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/__init__.py +0 -0
  67. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/api/__init__.py +0 -0
  68. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/api/admin/__init__.py +0 -0
  69. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/api/admin/test_api_discovery.py +0 -0
  70. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/api/admin/test_data_model.py +0 -0
  71. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/api/admin/test_goal.py +0 -0
  72. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/api/admin/test_scoring_engine.py +0 -0
  73. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/api/admin/test_service.py +0 -0
  74. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/api/admin/test_strategy.py +0 -0
  75. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/client_functional/__init__.py +0 -0
  76. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/client_functional/v1/__init__.py +0 -0
  77. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/client_functional/v1/base.py +0 -0
  78. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/client_functional/v1/test_action.py +0 -0
  79. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/client_functional/v1/test_action_plan.py +0 -0
  80. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/client_functional/v1/test_audit.py +0 -0
  81. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/client_functional/v1/test_audit_template.py +0 -0
  82. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/client_functional/v1/test_goal.py +0 -0
  83. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/client_functional/v1/test_scoring_engine.py +0 -0
  84. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/client_functional/v1/test_service.py +0 -0
  85. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/client_functional/v1/test_strategy.py +0 -0
  86. {watcher_tempest_plugin-3.6.0/watcher_tempest_plugin/tests/scenario → watcher_tempest_plugin-3.8.0/watcher_tempest_plugin/tests/common}/__init__.py +0 -0
  87. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/scenario/test_data_model.py +0 -0
  88. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/scenario/test_execute_actuator.py +0 -0
  89. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/scenario/test_execute_basic_optim.py +0 -0
  90. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/scenario/test_execute_dummy_optim.py +0 -0
  91. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/scenario/test_execute_host_maintenance.py +0 -0
  92. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/scenario/test_execute_node_resource_consolidation.py +0 -0
  93. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/scenario/test_execute_storage_balance.py +0 -0
  94. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/scenario/test_execute_strategies_real.py +0 -0
  95. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/scenario/test_execute_vm_workload_consolidation.py +0 -0
  96. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/scenario/test_execute_workload_balance.py +0 -0
  97. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/tests/scenario/test_execute_workload_stabilization.py +0 -0
  98. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin/utils.py +0 -0
  99. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin.egg-info/dependency_links.txt +0 -0
  100. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin.egg-info/entry_points.txt +0 -0
  101. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin.egg-info/not-zip-safe +0 -0
  102. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin.egg-info/requires.txt +0 -0
  103. {watcher_tempest_plugin-3.6.0 → watcher_tempest_plugin-3.8.0}/watcher_tempest_plugin.egg-info/top_level.txt +0 -0
@@ -0,0 +1,66 @@
1
+ - project:
2
+ queue: watcher
3
+ templates:
4
+ - check-requirements
5
+ - tempest-plugin-jobs
6
+ - publish-openstack-docs-pti
7
+ - release-notes-jobs-python3
8
+ check:
9
+ jobs:
10
+ - watcher-tempest-aetos
11
+ - watcher-tempest-gnocchi
12
+ - watcher-tempest-prometheus
13
+ - watcher-tempest-aetos-2026-1
14
+ - watcher-tempest-gnocchi-2026-1
15
+ - watcher-tempest-aetos-2025-2
16
+ - watcher-tempest-gnocchi-2025-2
17
+ - watcher-tempest-gnocchi-2025-1
18
+ - watcher-tempest-prometheus-2025-1
19
+ gate:
20
+ jobs:
21
+ - watcher-tempest-aetos
22
+ - watcher-tempest-gnocchi
23
+ - watcher-tempest-prometheus
24
+ - watcher-tempest-aetos-2026-1
25
+ - watcher-tempest-gnocchi-2026-1
26
+ - watcher-tempest-aetos-2025-2
27
+ - watcher-tempest-gnocchi-2025-2
28
+ - watcher-tempest-gnocchi-2025-1
29
+ - watcher-tempest-prometheus-2025-1
30
+ experimental:
31
+ jobs:
32
+ - watcher-tempest-prometheus-realdata
33
+ - watcher-tempest-aetos-realdata
34
+
35
+ # stable/2026.1 jobs
36
+ - job:
37
+ name: watcher-tempest-aetos-2026-1
38
+ parent: watcher-tempest-aetos
39
+ override-checkout: stable/2026.1
40
+
41
+ - job:
42
+ name: watcher-tempest-gnocchi-2026-1
43
+ parent: watcher-tempest-gnocchi
44
+ override-checkout: stable/2026.1
45
+
46
+ # stable/2025.2 jobs
47
+ - job:
48
+ name: watcher-tempest-aetos-2025-2
49
+ parent: watcher-tempest-aetos
50
+ override-checkout: stable/2025.2
51
+
52
+ - job:
53
+ name: watcher-tempest-gnocchi-2025-2
54
+ parent: watcher-tempest-gnocchi
55
+ override-checkout: stable/2025.2
56
+
57
+ # stable/2025.1 jobs
58
+ - job:
59
+ name: watcher-tempest-gnocchi-2025-1
60
+ parent: watcher-tempest-gnocchi
61
+ override-checkout: stable/2025.1
62
+
63
+ - job:
64
+ name: watcher-tempest-prometheus-2025-1
65
+ parent: watcher-tempest-prometheus
66
+ override-checkout: stable/2025.1
@@ -9,10 +9,12 @@ Dantali0n <info@dantalion.nl>
9
9
  David TARDIVEL <David.TARDIVEL@b-com.com>
10
10
  Douglas Viroel <viroel@gmail.com>
11
11
  Drew Thorstensen <thorst@us.ibm.com>
12
+ Ghanshyam Maan <gmaan@ghanshyammann.com>
12
13
  Ghanshyam Mann <gmann@ghanshyammann.com>
13
14
  Gábor Antal <antal@inf.u-szeged.hu>
14
15
  Hervé Beraud <hberaud@redhat.com>
15
16
  Hidekazu Nakamura <hid-nakamura@vf.jp.nec.com>
17
+ Ivan Anfimov <lazekteam@gmail.com>
16
18
  Jaewoo Park <jp655p@att.com>
17
19
  Jaromir Wysoglad <jwysogla@redhat.com>
18
20
  Ken'ichi Ohmichi <ken-oomichi@wx.jp.nec.com>
@@ -1,6 +1,33 @@
1
1
  CHANGES
2
2
  =======
3
3
 
4
+ 3.8.0
5
+ -----
6
+
7
+ * Remove 2024.2 jobs from the zuul gates
8
+ * Add stable/2026.1 job
9
+ * Parametrize promtool write URL path
10
+ * Add extra checks to zone migration retype test
11
+ * Test zone migration volume and compute migrations
12
+
13
+ 3.7.0
14
+ -----
15
+
16
+ * Add test for volume migrate with zone migration
17
+ * Add project badge
18
+ * Consolidate and improve Zuul CI jobs for stable branches
19
+ * Fix unsupported operand types in internal methods
20
+ * Refresh the state of the actionplan before asserting status
21
+ * Fix experimental job names after refactoring watcher jobs
22
+ * Add config flags to control zone migration test execution
23
+ * Skip test api.admin.test\_audit.TestShowListAudit.test\_list\_with\_limit
24
+ * Consolidate and improve Zuul CI job definitions
25
+ * Wait action plan to finish before asserting state
26
+ * Improved continuous audit api test coverage
27
+ * Refactor: Extract common test utilities into shared base class
28
+ * Drop job to test unmaintained 2024.1 branch
29
+ * Drop python 3.9 and support python 3.13
30
+
4
31
  3.6.0
5
32
  -----
6
33
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: watcher-tempest-plugin
3
- Version: 3.6.0
3
+ Version: 3.8.0
4
4
  Summary: Tempest plugin watcher-tempest-plugin
5
5
  Home-page: https://docs.openstack.org/watcher/latest/
6
6
  Author: OpenStack
@@ -14,22 +14,35 @@ Classifier: Programming Language :: Python
14
14
  Classifier: Programming Language :: Python :: Implementation :: CPython
15
15
  Classifier: Programming Language :: Python :: 3 :: Only
16
16
  Classifier: Programming Language :: Python :: 3
17
- Classifier: Programming Language :: Python :: 3.9
18
17
  Classifier: Programming Language :: Python :: 3.10
19
18
  Classifier: Programming Language :: Python :: 3.11
20
19
  Classifier: Programming Language :: Python :: 3.12
21
- Requires-Python: >=3.9
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Requires-Python: >=3.10
22
22
  License-File: LICENSE
23
23
  Requires-Dist: pbr>=3.1.1
24
24
  Requires-Dist: oslo.log>=3.36.0
25
25
  Requires-Dist: oslo.utils>=3.33.0
26
26
  Requires-Dist: tempest>=27.0.0
27
27
  Requires-Dist: os-traits>=0.15.0
28
+ Dynamic: author
29
+ Dynamic: author-email
30
+ Dynamic: classifier
31
+ Dynamic: description
32
+ Dynamic: home-page
33
+ Dynamic: license-file
34
+ Dynamic: requires-dist
35
+ Dynamic: requires-python
36
+ Dynamic: summary
28
37
 
29
38
  ======================
30
39
  watcher-tempest-plugin
31
40
  ======================
32
41
 
42
+ .. image:: https://governance.openstack.org/tc/badges/watcher-tempest-plugin.svg
43
+
44
+ .. Change things from this point on
45
+
33
46
  This repository contains a `Tempest`_ `test plugin`_ to verify the
34
47
  functionality of the `Watcher`_ service.
35
48
 
@@ -2,6 +2,10 @@
2
2
  watcher-tempest-plugin
3
3
  ======================
4
4
 
5
+ .. image:: https://governance.openstack.org/tc/badges/watcher-tempest-plugin.svg
6
+
7
+ .. Change things from this point on
8
+
5
9
  This repository contains a `Tempest`_ `test plugin`_ to verify the
6
10
  functionality of the `Watcher`_ service.
7
11
 
@@ -0,0 +1,8 @@
1
+ ---
2
+ features:
3
+ - |
4
+ Added a new ``prometheus_write_path`` configuration option under the
5
+ ``[optimize]`` section to allow customizing the URL path appended to the
6
+ Prometheus base URL for remote write operations. The default value is
7
+ ``/api/v1/write``, preserving the existing behavior. Set it to an empty
8
+ string if the base URL already includes the full write endpoint.
@@ -6,7 +6,7 @@ description_file =
6
6
  author = OpenStack
7
7
  author_email = openstack-discuss@lists.openstack.org
8
8
  home_page = https://docs.openstack.org/watcher/latest/
9
- python_requires = >=3.9
9
+ python_requires = >=3.10
10
10
  classifier =
11
11
  Environment :: OpenStack
12
12
  Intended Audience :: Information Technology
@@ -17,10 +17,10 @@ classifier =
17
17
  Programming Language :: Python :: Implementation :: CPython
18
18
  Programming Language :: Python :: 3 :: Only
19
19
  Programming Language :: Python :: 3
20
- Programming Language :: Python :: 3.9
21
20
  Programming Language :: Python :: 3.10
22
21
  Programming Language :: Python :: 3.11
23
22
  Programming Language :: Python :: 3.12
23
+ Programming Language :: Python :: 3.13
24
24
 
25
25
  [files]
26
26
  packages =
@@ -96,6 +96,13 @@ OptimizationGroup = [
96
96
  help="The label that Prometheus uses to store the fqdn of "
97
97
  "exporters.",
98
98
  ),
99
+ cfg.StrOpt(
100
+ "prometheus_write_path",
101
+ default="/api/v1/write",
102
+ help="The URL path to append to the Prometheus base URL for "
103
+ "remote write operations. Set to an empty string if the "
104
+ "base URL already includes the full write endpoint.",
105
+ ),
99
106
  # Podified control plane configuration
100
107
  cfg.StrOpt(
101
108
  "podified_kubeconfig_path",
@@ -144,4 +151,15 @@ OptimizationGroup = [
144
151
  "`watcher_cluster_data_model_collectors` period configuration is "
145
152
  "less than 300 seconds."
146
153
  ),
154
+ cfg.BoolOpt(
155
+ "run_zone_migration_extra_tests",
156
+ default=False,
157
+ help="Whether or not to run extra zone migration tests that test"
158
+ "migration without specified destination nodes."
159
+ ),
160
+ cfg.BoolOpt(
161
+ "run_zone_migration_storage_tests",
162
+ default=False,
163
+ help="Whether or not to run storage zone migration tests."
164
+ ),
147
165
  ]
@@ -55,6 +55,7 @@ class BaseManager(clients.Manager, metaclass=abc.ABCMeta):
55
55
  podified_kubeconfig=CONF.optimize.podified_kubeconfig_path,
56
56
  prometheus_ssl_cert=CONF.optimize.prometheus_ssl_cert_dir,
57
57
  prometheus_fqdn_label=CONF.optimize.prometheus_fqdn_label,
58
+ write_url_path=CONF.optimize.prometheus_write_path,
58
59
  )
59
60
 
60
61
 
@@ -13,9 +13,9 @@
13
13
  # under the License.
14
14
 
15
15
  import json
16
+ from urllib import parse
16
17
 
17
18
  from oslo_log import log
18
- from urllib.parse import quote
19
19
 
20
20
  from watcher_tempest_plugin.services import base
21
21
 
@@ -31,8 +31,32 @@ class PromtoolClient:
31
31
  proxy_host_pkey=None, proxy_host_pkey_type='rsa',
32
32
  podified_ns=None, podified_kubeconfig=None,
33
33
  prometheus_ssl_cert=None,
34
- prometheus_fqdn_label="fqdn"):
35
-
34
+ prometheus_fqdn_label="fqdn",
35
+ write_url_path=None):
36
+ """Initialize PromtoolClient.
37
+
38
+ :param url: Base URL of the Prometheus server (e.g.
39
+ 'http://host:9090').
40
+ :param promtool_path: Path to the promtool binary.
41
+ :param openstack_type: Deployment type; use 'podified' for
42
+ Podified Control Plane deployments, 'devstack' otherwise.
43
+ :param proxy_host_address: Optional SSH proxy host address. When
44
+ provided together with proxy_host_user, commands are executed
45
+ over SSH instead of locally.
46
+ :param proxy_host_user: SSH username for the proxy host.
47
+ :param proxy_host_pkey: Path to the SSH private key file.
48
+ :param proxy_host_pkey_type: Type of the SSH private key
49
+ (default: 'rsa').
50
+ :param podified_ns: Kubernetes namespace for podified deployments.
51
+ :param podified_kubeconfig: Path to the kubeconfig file for
52
+ podified deployments.
53
+ :param prometheus_ssl_cert: Directory containing SSL certificates
54
+ used when connecting to Prometheus over HTTPS.
55
+ :param prometheus_fqdn_label: Prometheus label used to identify
56
+ host FQDNs in target metadata (default: 'fqdn').
57
+ :param write_url_path: URL path for the remote-write endpoint
58
+ (default: '/api/v1/write').
59
+ """
36
60
  # Podified Control Plane
37
61
  self.is_podified = ("podified" == openstack_type)
38
62
  LOG.debug(f"Configuring PromtoolClient for {openstack_type} "
@@ -61,12 +85,23 @@ class PromtoolClient:
61
85
  cmd_prefix = self.oc_cmd + ["rsh", self.prometheus_pod]
62
86
  self.client.cmd_prefix = " ".join(cmd_prefix)
63
87
 
88
+ # We keep the raw URL for reference/labels
64
89
  self.prometheus_url = url
65
- self.prometheus_write_url = url + "/api/v1/write"
66
- self.prometheus_delete_series_url = (
67
- url + "/api/v1/admin/tsdb/delete_series")
90
+
91
+ # Internal base_url ensuring a trailing slash for reliable joining
92
+ base_url = url if url.endswith('/') else url + '/'
93
+
94
+ # Handle write_url_path: handles None/Empty and leading slash issues
95
+ clean_write_path = (write_url_path or "").lstrip('/')
96
+ self.prometheus_write_url = parse.urljoin(base_url, clean_write_path)
97
+
98
+ self.prometheus_delete_series_url = parse.urljoin(
99
+ base_url, "api/v1/admin/tsdb/delete_series"
100
+ )
68
101
  # active targets
69
- self.prometheus_targets_url = url + "/api/v1/targets?state=active"
102
+ self.prometheus_targets_url = parse.urljoin(
103
+ base_url, "api/v1/targets?state=active"
104
+ )
70
105
  self.prometheus_fqdn_label = prometheus_fqdn_label
71
106
  self.promtool_cmd = [promtool_path]
72
107
  if prometheus_ssl_cert:
@@ -137,7 +172,7 @@ class PromtoolClient:
137
172
  # with double quotes both on direct command and
138
173
  # ssh command so using url encoding
139
174
 
140
- quoted_expr = quote(expr)
175
+ quoted_expr = parse.quote(expr)
141
176
 
142
177
  cmd = ['curl', '-s', '-k', '-X', 'POST', '-g',
143
178
  self.prometheus_delete_series_url + '?match[]=' + quoted_expr]
@@ -0,0 +1,114 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # Copyright (c) 2016 b<>com
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13
+ # implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ from tempest import config
18
+ from tempest.lib.common import api_version_utils
19
+ from tempest.lib.common.utils import test_utils
20
+ from tempest import test
21
+
22
+ from watcher_tempest_plugin import infra_optim_clients as clients
23
+ from watcher_tempest_plugin.services.infra_optim.v1.json import (
24
+ api_microversion_fixture
25
+ )
26
+ from watcher_tempest_plugin.tests.common import base
27
+
28
+ CONF = config.CONF
29
+
30
+
31
+ class BaseInfraOptimTest(api_version_utils.BaseMicroversionTest,
32
+ test.BaseTestCase,
33
+ base.WatcherHelperMixin):
34
+ """Base class for Infrastructure Optimization API tests."""
35
+
36
+ @classmethod
37
+ def skip_checks(cls):
38
+ super(BaseInfraOptimTest, cls).skip_checks()
39
+ if not CONF.service_available.watcher:
40
+ raise cls.skipException('Watcher support is required')
41
+
42
+ api_version_utils.check_skip_with_microversion(
43
+ cls.min_microversion,
44
+ cls.max_microversion,
45
+ CONF.optimize.min_microversion,
46
+ CONF.optimize.max_microversion)
47
+
48
+ @classmethod
49
+ def setup_credentials(cls):
50
+ super(BaseInfraOptimTest, cls).setup_credentials()
51
+ cls.mgr = clients.AdminManager()
52
+
53
+ @classmethod
54
+ def setup_clients(cls):
55
+ super(BaseInfraOptimTest, cls).setup_clients()
56
+ cls.client = cls.mgr.io_client
57
+ cls.gnocchi = cls.mgr.gn_client
58
+
59
+ def setUp(self):
60
+ super(BaseInfraOptimTest, self).setUp()
61
+ self.useFixture(api_microversion_fixture.APIMicroversionFixture(
62
+ optimize_microversion=self.request_microversion))
63
+
64
+ @classmethod
65
+ def resource_setup(cls):
66
+ super(BaseInfraOptimTest, cls).resource_setup()
67
+
68
+ # Set of all created audit templates UUIDs
69
+ cls.created_audit_templates = set()
70
+ # Set of all created audit UUIDs
71
+ cls.created_audits = set()
72
+ # Set of all created audit UUIDs. We use it to build the list of
73
+ # action plans to delete (including potential orphan one(s))
74
+ cls.created_action_plans_audit_uuids = set()
75
+
76
+ cls.request_microversion = (
77
+ api_version_utils.select_request_microversion(
78
+ cls.min_microversion,
79
+ CONF.optimize.min_microversion))
80
+
81
+ @classmethod
82
+ def resource_cleanup(cls):
83
+ """Ensure that all created objects get destroyed."""
84
+ super(BaseInfraOptimTest, cls).resource_cleanup()
85
+
86
+ def _are_all_action_plans_finished(self):
87
+ _, action_plans = self.client.list_action_plans()
88
+ return all([ap['state'] in self.IDLE_STATES.values()
89
+ for ap in action_plans['action_plans']])
90
+
91
+ def wait_for_all_action_plans_to_finish(self):
92
+ assert test_utils.call_until_true(
93
+ func=self._are_all_action_plans_finished,
94
+ duration=300,
95
+ sleep_for=5
96
+ )
97
+
98
+ def validate_self_link(self, resource, uuid, link):
99
+ """Check whether the given self link formatted correctly."""
100
+ expected_link = "{base}/{pref}/{res}/{uuid}".format(
101
+ base='https?://[^/]*',
102
+ pref=self.client.URI_PREFIX,
103
+ res=resource,
104
+ uuid=uuid
105
+ )
106
+ self.assertRegex(link, expected_link)
107
+
108
+ def assert_expected(self, expected, actual,
109
+ keys=('created_at', 'updated_at', 'deleted_at')):
110
+ # Check if not expected keys/values exists in actual response body
111
+ for key, value in expected.items():
112
+ if key not in keys:
113
+ self.assertIn(key, actual)
114
+ self.assertEqual(value, actual[key])
@@ -26,21 +26,36 @@ from watcher_tempest_plugin.tests.api.admin import base
26
26
  class TestShowListAction(base.BaseInfraOptimTest):
27
27
  """Tests for actions"""
28
28
 
29
- @classmethod
30
- def resource_setup(cls):
31
- super(TestShowListAction, cls).resource_setup()
32
- _, cls.goal = cls.client.show_goal("DUMMY")
33
- _, cls.audit_template = cls.create_audit_template(cls.goal['uuid'])
34
- _, cls.audit = cls.create_audit(cls.audit_template['uuid'])
29
+ def setUp(self):
30
+ super().setUp()
31
+ _, self.goal = self.client.show_goal("DUMMY")
32
+ _, self.audit_template = self.create_audit_template(self.goal['uuid'])
35
33
 
34
+ # Wait for any running action plans to finish before creating audit
35
+ self.wait_for_all_action_plans_to_finish()
36
+
37
+ _, self.audit = self.create_audit(self.audit_template['uuid'])
38
+
39
+ # Wait for audit to finish (in any state)
36
40
  assert test_utils.call_until_true(
37
- func=functools.partial(cls.has_audit_finished, cls.audit['uuid']),
41
+ func=functools.partial(self.has_audit_finished,
42
+ self.audit['uuid']),
38
43
  duration=30,
39
44
  sleep_for=.5
45
+ ), "Audit %s did not finish within expected time" % self.audit['uuid']
46
+
47
+ # Check if audit succeeded
48
+ _, finished_audit = self.client.show_audit(self.audit['uuid'])
49
+ audit_state = finished_audit.get('state')
50
+ assert audit_state == 'SUCCEEDED', (
51
+ "Audit %s ended in state %s instead of SUCCEEDED" %
52
+ (self.audit['uuid'], audit_state)
40
53
  )
41
- _, action_plans = cls.client.list_action_plans(
42
- audit_uuid=cls.audit['uuid'])
43
- cls.action_plan = action_plans['action_plans'][0]
54
+
55
+ _, action_plans = self.client.list_action_plans(
56
+ audit_uuid=self.audit['uuid'])
57
+
58
+ self.action_plan = action_plans['action_plans'][0]
44
59
 
45
60
  @decorators.attr(type='smoke')
46
61
  @decorators.idempotent_id('0390c465-df10-460f-9924-ff3d14779395')
@@ -119,25 +134,36 @@ class TestPatchAction(base.BaseInfraOptimTest):
119
134
  # Minimal version required for Actions Patch API
120
135
  min_microversion = "1.5"
121
136
 
122
- @classmethod
123
- def setup(cls):
124
- super(TestPatchAction, cls).setup()
137
+ def setUp(self):
138
+ super().setUp()
139
+ _, self.goal = self.client.show_goal("DUMMY")
140
+ _, self.audit_template = self.create_audit_template(self.goal['uuid'])
125
141
 
126
- @classmethod
127
- def resource_setup(cls):
128
- super(TestPatchAction, cls).resource_setup()
129
- _, cls.goal = cls.client.show_goal("DUMMY")
130
- _, cls.audit_template = cls.create_audit_template(cls.goal['uuid'])
131
- _, cls.audit = cls.create_audit(cls.audit_template['uuid'])
142
+ # Wait for any running action plans to finish before creating audit
143
+ self.wait_for_all_action_plans_to_finish()
132
144
 
145
+ _, self.audit = self.create_audit(self.audit_template['uuid'])
146
+
147
+ # Wait for audit to finish (in any state)
133
148
  assert test_utils.call_until_true(
134
- func=functools.partial(cls.has_audit_finished, cls.audit['uuid']),
149
+ func=functools.partial(self.has_audit_finished,
150
+ self.audit['uuid']),
135
151
  duration=30,
136
152
  sleep_for=.5
153
+ ), "Audit %s did not finish within expected time" % self.audit['uuid']
154
+
155
+ # Check if audit succeeded
156
+ _, finished_audit = self.client.show_audit(self.audit['uuid'])
157
+ audit_state = finished_audit.get('state')
158
+ assert audit_state == 'SUCCEEDED', (
159
+ "Audit %s ended in state %s instead of SUCCEEDED" %
160
+ (self.audit['uuid'], audit_state)
137
161
  )
138
- _, action_plans = cls.client.list_action_plans(
139
- audit_uuid=cls.audit['uuid'])
140
- cls.action_plan = action_plans['action_plans'][0]
162
+
163
+ _, action_plans = self.client.list_action_plans(
164
+ audit_uuid=self.audit['uuid'])
165
+
166
+ self.action_plan = action_plans['action_plans'][0]
141
167
 
142
168
  @decorators.attr(type="smoke")
143
169
  @decorators.idempotent_id('e2360342-8042-4faf-a028-5e63fd844387')
@@ -52,6 +52,10 @@ class TestCreateDeleteExecuteActionPlan(base.BaseInfraOptimTest):
52
52
  def test_execute_action_plan(self):
53
53
  _, goal = self.client.show_goal("dummy")
54
54
  _, audit_template = self.create_audit_template(goal['uuid'])
55
+
56
+ # Wait for any running action plans to finish before creating audit
57
+ self.wait_for_all_action_plans_to_finish()
58
+
55
59
  _, audit = self.create_audit(audit_template['uuid'])
56
60
 
57
61
  self.assertTrue(test_utils.call_until_true(
@@ -59,6 +63,7 @@ class TestCreateDeleteExecuteActionPlan(base.BaseInfraOptimTest):
59
63
  duration=30,
60
64
  sleep_for=.5
61
65
  ))
66
+
62
67
  _, action_plans = self.client.list_action_plans(
63
68
  audit_uuid=audit['uuid'])
64
69
  action_plan = action_plans['action_plans'][0]
@@ -76,6 +81,10 @@ class TestCreateDeleteExecuteActionPlan(base.BaseInfraOptimTest):
76
81
  def test_delete_action_plan(self):
77
82
  _, goal = self.client.show_goal("dummy")
78
83
  _, audit_template = self.create_audit_template(goal['uuid'])
84
+
85
+ # Wait for any running action plans to finish before creating audit
86
+ self.wait_for_all_action_plans_to_finish()
87
+
79
88
  _, audit = self.create_audit(audit_template['uuid'])
80
89
 
81
90
  self.assertTrue(test_utils.call_until_true(
@@ -83,6 +92,7 @@ class TestCreateDeleteExecuteActionPlan(base.BaseInfraOptimTest):
83
92
  duration=30,
84
93
  sleep_for=.5
85
94
  ))
95
+
86
96
  _, action_plans = self.client.list_action_plans(
87
97
  audit_uuid=audit['uuid'])
88
98
  action_plan = action_plans['action_plans'][0]
@@ -98,22 +108,27 @@ class TestCreateDeleteExecuteActionPlan(base.BaseInfraOptimTest):
98
108
  class TestShowListActionPlan(base.BaseInfraOptimTest):
99
109
  """Tests for action_plan."""
100
110
 
101
- @classmethod
102
- def resource_setup(cls):
103
- super(TestShowListActionPlan, cls).resource_setup()
104
- _, cls.goal = cls.client.show_goal("dummy")
105
- _, cls.audit_template = cls.create_audit_template(cls.goal['uuid'])
106
- _, cls.audit = cls.create_audit(cls.audit_template['uuid'])
111
+ def setUp(self):
112
+ super().setUp()
113
+ _, self.goal = self.client.show_goal("dummy")
114
+ _, self.audit_template = self.create_audit_template(self.goal['uuid'])
115
+
116
+ # Wait for any running action plans to finish before creating audit
117
+ self.wait_for_all_action_plans_to_finish()
118
+
119
+ _, self.audit = self.create_audit(self.audit_template['uuid'])
107
120
 
108
121
  assert test_utils.call_until_true(
109
- func=functools.partial(cls.has_audit_finished, cls.audit['uuid']),
122
+ func=functools.partial(
123
+ self.has_audit_finished, self.audit['uuid']),
110
124
  duration=30,
111
125
  sleep_for=.5
112
126
  )
113
- _, action_plans = cls.client.list_action_plans(
114
- audit_uuid=cls.audit['uuid'])
127
+
128
+ _, action_plans = self.client.list_action_plans(
129
+ audit_uuid=self.audit['uuid'])
115
130
  if len(action_plans['action_plans']) > 0:
116
- cls.action_plan = action_plans['action_plans'][0]
131
+ self.action_plan = action_plans['action_plans'][0]
117
132
 
118
133
  @decorators.attr(type='smoke')
119
134
  @decorators.idempotent_id('33465228-aac1-4688-9db3-6b1532c84323')