Skip to content

Commit f5bcdfe

Browse files
committed
Enhance chargeback synthetic script and temple
- Used Gemini and Cursor - add scenario(parameter) files. - Each scenrio file defines one test scenario with specific metrics - Scenario file names begin with "test_" - All scenerio files are found and read automatically - Output files have same name as scenario but different extension - Price and qty variable are not dynamic => "change over time" - Closes https://issues.redhat.com/browse/OSPRH-24867 - Closes https://issues.redhat.com/browse/OSPRH-24865
1 parent ba92755 commit f5bcdfe

File tree

10 files changed

+334
-46
lines changed

10 files changed

+334
-46
lines changed

roles/telemetry_chargeback/README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,24 @@ It is expected to be run **after** a successful deployment and configuration of
1818

1919
Role Variables
2020
--------------
21-
The role uses a few primary variables to control the testing environment and execution.
21+
The role uses the following variables to control the testing environment and execution.
22+
23+
### User-Configurable Variables (defaults/main.yml)
2224

2325
| Variable | Default Value | Description |
2426
|----------|---------------|-------------|
2527
| `openstack_cmd` | `openstack` | The command used to execute OpenStack CLI calls. This can be customized if the binary is not in the standard PATH. |
2628

29+
### Internal Variables (vars/main.yml)
30+
31+
| Variable | Default Value | Description |
32+
|----------|---------------|-------------|
33+
| `logs_dir_zuul` | `/home/zuul/ci-framework-data/logs` | Remote directory for log files. |
34+
| `artifacts_dir_zuul` | `/home/zuul/ci-framework-data/artifacts` | Remote directory for generated artifacts. |
35+
| `ck_scenario_dir` | `{{ role_path }}/files` | Local directory containing scenario files (test_*.yml). |
36+
| `ck_synth_script` | `{{ role_path }}/files/gen_synth_loki_data.py` | Path to the Python script that generates synthetic Loki data. |
37+
| `ck_data_template` | `{{ role_path }}/template/loki_data_templ.j2` | Path to the Jinja2 template for Loki data format. |
38+
2739
Dependencies
2840
------------
2941
This role has no direct hard dependencies on other Ansible roles.
Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,2 @@
11
---
22
openstack_cmd: "openstack"
3-
4-
ck_synth_script: "{{ role_path }}/files/gen_synth_loki_data.py"
5-
ck_data_template: "{{ role_path }}/files/loki_data_templ.j2"
6-
ck_data_config: "{{ role_path }}/files/test_static.yml"
7-
ck_output_file_local: "{{ role_path }}/files/loki_synth_data.json"
8-
9-
# Output directory for test artifacts
10-
logs_dir: "/home/zuul/ci-framework-data/tests/feature-verification-tests"

roles/telemetry_chargeback/files/gen_synth_loki_data.py

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,44 @@
55
import yaml
66
from datetime import datetime, timezone, timedelta
77
from pathlib import Path
8-
from typing import Dict, Any
8+
from typing import Dict, Any, List, Union
99
from jinja2 import Environment
1010

1111

12+
def _get_value_for_step(
13+
values: List[Union[int, float]],
14+
step_idx: int,
15+
num_steps: int
16+
) -> Union[int, float]:
17+
"""
18+
Get the appropriate value from a list based on the current step index.
19+
20+
Values are distributed evenly across all steps. For example, if there are
21+
12 steps and 4 values, each value covers 3 steps:
22+
- Steps 0-2: values[0]
23+
- Steps 3-5: values[1]
24+
- Steps 6-8: values[2]
25+
- Steps 9-11: values[3]
26+
27+
Args:
28+
values: List of values to choose from.
29+
step_idx: Current step index (0-based).
30+
num_steps: Total number of steps.
31+
32+
Returns:
33+
The value corresponding to the current step.
34+
"""
35+
num_values = len(values)
36+
if num_values == 1:
37+
return values[0]
38+
39+
# Calculate how many steps each value covers
40+
steps_per_value = num_steps / num_values
41+
# Determine which value index to use, clamping to valid range
42+
value_idx = min(int(step_idx // steps_per_value), num_values - 1)
43+
return values[value_idx]
44+
45+
1246
# --- Configure logging with a default level that can be changed ---
1347
logging.basicConfig(
1448
level=logging.INFO,
@@ -86,9 +120,8 @@ def generate_loki_data(
86120
time_step_seconds (int): The duration of each log entry in seconds.
87121
config (Dict[str, Any]): Configuration dictionary loaded from file.
88122
"""
89-
# Get constants from config
90-
constants = config.get("constants", {})
91-
invalid_timestamp = constants.get("invalid_timestamp", "INVALID_TIMESTAMP")
123+
# Hardcoded constant for invalid timestamp display
124+
invalid_timestamp = "INVALID_TIMESTAMP"
92125

93126
# --- Step 1: Generate the data structure first ---
94127
logger.info(
@@ -201,12 +234,18 @@ def generate_loki_data(
201234
f"groupby must be a dictionary for {log_type_name}"
202235
)
203236

237+
# Ensure qty and price are lists for step-based distribution
238+
qty_val = log_type_config["qty"]
239+
price_val = log_type_config["price"]
240+
qty_list = qty_val if isinstance(qty_val, list) else [qty_val]
241+
price_list = price_val if isinstance(price_val, list) else [price_val]
242+
204243
log_types[log_type_name] = {
205244
"type": log_type_config["type"],
206245
"unit": log_type_config["unit"],
207246
"description": log_type_config.get("description"),
208-
"qty": log_type_config["qty"],
209-
"price": log_type_config["price"],
247+
"qty": qty_list,
248+
"price": price_list,
210249
"groupby": groupby.copy(),
211250
"metadata": log_type_config.get("metadata", {})
212251
}
@@ -232,6 +271,10 @@ def tojson_preserve_order(obj):
232271
# --- Render the template in one pass with all the data ---
233272
logger.info("Rendering final output...")
234273

274+
# Calculate total number of steps for value distribution
275+
num_steps = len(log_data_list)
276+
logger.debug(f"Total number of time steps: {num_steps}")
277+
235278
# Pre-calculate log types with date fields for each time step
236279
log_types_list = []
237280
for idx, item in enumerate(log_data_list):
@@ -268,6 +311,13 @@ def tojson_preserve_order(obj):
268311
log_type_with_dates = log_type_data.copy()
269312
log_type_with_dates["groupby"] = log_type_data["groupby"].copy()
270313
log_type_with_dates["groupby"].update(date_fields)
314+
# Select qty and price based on step index distribution
315+
log_type_with_dates["qty"] = _get_value_for_step(
316+
log_type_data["qty"], idx, num_steps
317+
)
318+
log_type_with_dates["price"] = _get_value_for_step(
319+
log_type_data["price"], idx, num_steps
320+
)
271321
log_types_with_dates[log_type_name] = log_type_with_dates
272322

273323
log_types_list.append(log_types_with_dates)
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Scenario configuration for synthetic Loki log data generation
2+
3+
# Time range configuration
4+
generation:
5+
days: 30
6+
step_seconds: 3600
7+
8+
# Log type definitions
9+
log_types:
10+
- name: ceilometer_cpu
11+
type: instance
12+
unit: instance
13+
description: "max number of cpus used in time step"
14+
qty:
15+
- 0.0
16+
price:
17+
- 0.3
18+
groupby:
19+
id: null
20+
user_id: null
21+
project_id: null
22+
metadata:
23+
flavor_name: null
24+
flavor_id: ""
25+
vcpus: ""
26+
27+
- name: ceilometer_image_size
28+
type: ceilometer_image_size
29+
unit: MiB
30+
description: "Size of ceilometer image"
31+
qty:
32+
- 0.0
33+
price:
34+
- 0.02
35+
groupby:
36+
id: null
37+
user_id: null
38+
project_id: null
39+
metadata:
40+
container_format: null
41+
disk_format: null
42+
43+
- name: ceilometer_disk_ephemeral_size
44+
type: null
45+
unit: GiB
46+
description: "Max at each timestep"
47+
qty:
48+
- 0.0
49+
price:
50+
- 0.1
51+
groupby:
52+
id: null
53+
user_id: null
54+
project_id: null
55+
metadata:
56+
type: null
57+
58+
- name: ceilometer_disk_root_size
59+
type: null
60+
unit: GiB
61+
description: null
62+
qty:
63+
- 0.0
64+
price:
65+
- 1.00
66+
groupby:
67+
id: null
68+
user_id: null
69+
project_id: null
70+
metadata:
71+
disk_format: null
72+
73+
- name: ceilometer_network_outgoing_bytes
74+
type: null
75+
unit: B
76+
description: null
77+
qty:
78+
- 0.0
79+
price:
80+
- 0.02
81+
groupby:
82+
id: null
83+
user_id: null
84+
project_id: null
85+
metadata:
86+
vm_instance: null
87+
88+
- name: ceilometer_network_incoming_bytes
89+
type: null
90+
unit: B
91+
description: null
92+
qty:
93+
- 0.0
94+
price:
95+
- 0.02
96+
groupby:
97+
id: null
98+
user_id: null
99+
project_id: null
100+
metadata:
101+
vm_instance: null
102+
103+
- name: ceilometer_ip_floating
104+
type: null
105+
unit: ip
106+
description: null
107+
qty:
108+
- 0.0
109+
price:
110+
- 0.50
111+
groupby:
112+
id: null
113+
user_id: null
114+
project_id: null
115+
metadata:
116+
state: null
117+
118+
# Required fields for validation (top-level fields only, not nested in groupby)
119+
required_fields:
120+
- type
121+
- unit
122+
- qty
123+
- price
124+
- groupby
125+
- metadata
126+
127+
# Date field names to add to groupby
128+
date_fields:
129+
- week_of_the_year
130+
- day_of_the_year
131+
- month
132+
- year
133+
134+
# Loki stream configuration
135+
loki_stream:
136+
service: cloudkitty
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Scenario configuration for synthetic Loki log data generation
2+
3+
# Time range configuration
4+
generation:
5+
days: 1
6+
step_seconds: 7200
7+
8+
# Log type definitions
9+
log_types:
10+
- name: ceilometer_image_size
11+
type: ceilometer_image_size
12+
unit: MiB
13+
description: null
14+
qty:
15+
- 20.6
16+
- 25.0
17+
- 26.0
18+
- 30.5
19+
price:
20+
- 0.02
21+
- 0.03
22+
- 0.04
23+
- 0.07
24+
- 0.10
25+
groupby:
26+
id: cd65d30f-8b94-4fa3-95dc-e3b429f479b2
27+
project_id: 0030775de80e4d84a4fd0d73e0a1b3a7
28+
user_id: null
29+
metadata:
30+
container_format: bare
31+
disk_format: qcow2
32+
33+
- name: instance
34+
type: instance
35+
unit: instance
36+
description: null
37+
qty:
38+
- 1.0
39+
price:
40+
- 0.3
41+
groupby:
42+
id: de168c31-ed44-4a1a-a079-51bd238a91d6
43+
project_id: 9cf5bcfc61a24682acc448af2d062ad2
44+
user_id: c29ab6e886354bbd88ee9899e62d1d40
45+
metadata:
46+
flavor_name: m1.tiny
47+
flavor_id: "1"
48+
vcpus: ""
49+
50+
# Required fields for validation (top-level fields only, not nested in groupby)
51+
required_fields:
52+
- type
53+
- unit
54+
- qty
55+
- price
56+
- groupby
57+
- metadata
58+
59+
# Date field names to add to groupby
60+
date_fields:
61+
- week_of_the_year
62+
- day_of_the_year
63+
- month
64+
- year
65+
66+
# Loki stream configuration
67+
loki_stream:
68+
service: cloudkitty

roles/telemetry_chargeback/files/test_static.yml

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
22

33
# Time range configuration
44
generation:
5-
days: 5
6-
step_seconds: 300
5+
days: 1
6+
step_seconds: 3600
77

88
# Log type definitions
99
log_types:
1010
- name: ceilometer_image_size
1111
type: ceilometer_image_size
1212
unit: MiB
1313
description: null
14-
qty: 20.6
15-
price: 0.02
14+
qty:
15+
- 20.6
16+
price:
17+
- 0.02
1618
groupby:
1719
id: cd65d30f-8b94-4fa3-95dc-e3b429f479b2
1820
project_id: 0030775de80e4d84a4fd0d73e0a1b3a7
@@ -25,8 +27,10 @@ log_types:
2527
type: instance
2628
unit: instance
2729
description: null
28-
qty: 1.0
29-
price: 0.3
30+
qty:
31+
- 1.0
32+
price:
33+
- 0.3
3034
groupby:
3135
id: de168c31-ed44-4a1a-a079-51bd238a91d6
3236
project_id: 9cf5bcfc61a24682acc448af2d062ad2
@@ -55,7 +59,3 @@ date_fields:
5559
# Loki stream configuration
5660
loki_stream:
5761
service: cloudkitty
58-
59-
# Error messages and constants
60-
constants:
61-
invalid_timestamp: "INVALID_TIMESTAMP"

0 commit comments

Comments
 (0)