Skip to content

Commit 9cb7c42

Browse files
committed
Add MonitorResourceAPIServerRequests, release 0.1.1.2
1 parent 322277c commit 9cb7c42

File tree

5 files changed

+218
-6
lines changed

5 files changed

+218
-6
lines changed

README.md

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
WIP
44

5-
# ocp_scale_utilites.threaded
5+
## ocp_scale_utilities.threaded
66
* https://docs.python.org/3/library/concurrent.futures.html
77
* https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor
88

99

10-
## Usage
10+
### Usage
1111
```
1212
from ocp_resources.virtual_machine import VirtualMachine
1313
from ocp_scale_utilities.threaded.utils import (
@@ -35,3 +35,27 @@ def funcB():
3535
with ThreadedScaleResources(resources=vms, wait_for_status=VirtualMachine.Status.RUNNING):
3636
yield vms
3737
```
38+
39+
## ocp_scale_utilities.monitoring
40+
41+
### Usage
42+
43+
```
44+
from ocp_resources.virtual_machine import VirtualMachine
45+
from ocp_scale_utilities.monitoring import MonitorResourceAPIServerRequests
46+
from ocp_scale_utilities.threaded.scale import ThreadedScaleResources
47+
from ocp_utilities.monitoring import Prometheus
48+
49+
monitor_api_requests = MonitorResourceAPIServerRequests(
50+
prometheus=Prometheus(...),
51+
resource_class=VirtualMachine,
52+
idle_requests_value=int(virtual_machine_resource_idle),
53+
)
54+
55+
monitor_api_requests.wait_for_idle()
56+
with ThreadedScaleResources(resources=vms):
57+
monitor_api_requests.wait_for_idle()
58+
yield vms
59+
monitor_api_requests.wait_for_idle()
60+
61+
```

ocp_scale_utilities/constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1+
TIMEOUT_30SEC = 30
2+
13
TIMEOUT_2MIN = 60 * 2
4+
TIMEOUT_5MIN = 60 * 5

ocp_scale_utilities/monitoring.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
from __future__ import annotations
2+
3+
import logging
4+
5+
from ocp_resources.resource import Resource
6+
from ocp_utilities.monitoring import Prometheus
7+
from timeout_sampler import TimeoutExpiredError, TimeoutSampler
8+
9+
from ocp_scale_utilities.constants import TIMEOUT_5MIN, TIMEOUT_30SEC
10+
11+
LOGGER = logging.getLogger(__name__)
12+
13+
14+
class MonitorResourceAPIServerRequests:
15+
def __init__(
16+
self,
17+
prometheus: Prometheus,
18+
resource_class: type[Resource],
19+
idle_requests_value: float,
20+
time_duration_seconds: int = TIMEOUT_5MIN,
21+
):
22+
"""
23+
Monitor API Server Requests for a particular Resource
24+
25+
Args:
26+
prometheus (Prometheus): Prometheus object from ocp_utilities
27+
resource_class (Resource): Resource class to monitor
28+
time_duration_seconds (int, optional): Time duration to use with prometheus query
29+
idle_requests_value (float, optional): Minimum value indicating an 'idle' state
30+
"""
31+
self.prometheus = prometheus
32+
self.resource_class = resource_class
33+
self.time_duration_seconds = time_duration_seconds
34+
self.idle_requests_value = idle_requests_value
35+
36+
self.apiserver_requests_query = (
37+
"sum by (resource) (rate(apiserver_request_total{"
38+
f'group="{self.resource_class.api_group}",'
39+
f'resource="{self.resource_class.kind.lower()}s"'
40+
f"}}[{self.time_duration_seconds}s]))"
41+
)
42+
43+
def _initial_wait(self) -> None:
44+
"""
45+
The initial state is unknown, and will be unknown, it cannot be guaranteed.
46+
The code calling this could be the first code that upsets the cluster, or not.
47+
Wait for the cluster to either stay silent or become noisy.
48+
"""
49+
initial_silence_count = 0
50+
initial_noise_count = 0
51+
sampler = TimeoutSampler(
52+
wait_timeout=TIMEOUT_30SEC,
53+
sleep=5,
54+
func=self.prometheus.query_sampler,
55+
query=self.apiserver_requests_query,
56+
)
57+
try:
58+
for sample in sampler:
59+
if sample:
60+
value = float(sample[0]["value"][1])
61+
if value < self.idle_requests_value:
62+
initial_noise_count = 0
63+
initial_silence_count += 1
64+
else:
65+
initial_silence_count = 0
66+
initial_noise_count += 1
67+
68+
if initial_silence_count > 5 or initial_noise_count > 2:
69+
break
70+
except TimeoutExpiredError:
71+
pass
72+
73+
def wait_for_idle(self) -> None:
74+
"""
75+
Wait for 'idle' cluster state based on provided Resource
76+
"""
77+
self._initial_wait()
78+
79+
sampler = TimeoutSampler(
80+
wait_timeout=self.time_duration_seconds * 2,
81+
sleep=5,
82+
func=self.prometheus.query_sampler,
83+
query=self.apiserver_requests_query,
84+
)
85+
sample = None
86+
try:
87+
for sample in sampler:
88+
if sample and float(sample[0]["value"][1]) < self.idle_requests_value:
89+
return
90+
except TimeoutExpiredError:
91+
LOGGER.error(
92+
f"Metric value: {sample} of {self.apiserver_requests_query!r} "
93+
f"is not below minimum: {self.idle_requests_value}"
94+
)
95+
raise

pyproject.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ addopts = [
88
"--cov-config=pyproject.toml",
99
"--cov-report=html",
1010
"--cov-report=term",
11-
"--cov=ocp_scale_utilites.threaded.scale",
12-
"--cov=ocp_scale_utilites.threaded.utils",
11+
"--cov=ocp_scale_utilities.monitoring",
12+
"--cov=ocp_scale_utilities.threaded.scale",
13+
"--cov=ocp_scale_utilities.threaded.utils",
1314
]
1415

1516
[tool.coverage.run]
@@ -47,7 +48,7 @@ dev-dependencies = [ "ipdb>=0.13.13", "ipython>=8.12.3" ]
4748
[project]
4849
name = "openshift-python-scale-utilities"
4950
requires-python = ">=3.12.0"
50-
version = "0.1.1.1"
51+
version = "0.1.1.2"
5152
description = "OpenShift Python Scale Utilities"
5253
readme = "README.md"
5354
license = "Apache-2.0"
@@ -60,6 +61,7 @@ dependencies = [
6061
"pytest>=8.3.3",
6162
"timeout-sampler>=1.0.1",
6263
"openshift-python-wrapper~=11.0.10",
64+
"openshift-python-utilities>=6.0.0",
6365
]
6466

6567

uv.lock

Lines changed: 89 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)