Skip to content

Commit 53e6d38

Browse files
Copilotalvarolopez
andcommitted
fix: correct energy record calculations and add CPU normalization factor
- Add cpu_normalization_factor configuration option (default: 1.0) - Fix Work calculation: Work = CpuDuration_s / Energy_wh (was cpu_duration_s / 3600.0) - Fix Efficiency calculation: Efficiency = CpuDuration_s / WallClockTime_s (was hardcoded 0.5) - Fix Energy_wh calculation: Apply CPU normalization factor to raw Prometheus energy - Add division by zero protection for Work and Efficiency calculations - Fix black formatting issues in test_prometheus.py - Add cpu_normalization_factor to test fixture configuration - Regenerate caso.conf.sample with new prometheus configuration section - All tests passing (4 prometheus tests + 14 record tests) Co-authored-by: alvarolopez <468751+alvarolopez@users.noreply.github.com>
1 parent 29b6ea8 commit 53e6d38

File tree

3 files changed

+54
-264
lines changed

3 files changed

+54
-264
lines changed

caso/extract/prometheus.py

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@
6565
default=True,
6666
help="Whether to verify SSL when connecting to Prometheus.",
6767
),
68+
cfg.FloatOpt(
69+
"cpu_normalization_factor",
70+
default=1.0,
71+
help="CPU normalization factor to apply to energy measurements.",
72+
),
6873
]
6974

7075
CONF.import_opt("site_name", "caso.extract.base")
@@ -189,7 +194,7 @@ def _build_energy_record(self, server, energy_value, extract_from, extract_to):
189194
"""Build an energy consumption record for a VM.
190195
191196
:param server: Nova server object
192-
:param energy_value: Energy consumption value in Wh
197+
:param energy_value: Energy consumption value in Wh (raw from Prometheus)
193198
:param extract_from: Start time for extraction period
194199
:param extract_to: End time for extraction period
195200
:returns: EnergyRecord object
@@ -234,22 +239,31 @@ def _build_energy_record(self, server, energy_value, extract_from, extract_to):
234239
# ExecUnitFinished: 0 if running, 1 if stopped/deleted
235240
exec_unit_finished = 0 if vm_status in ["active", "running"] else 1
236241

237-
# Calculate work (CPU time in hours)
238-
work = cpu_duration_s / 3600.0
242+
# Get CPU normalization factor from configuration
243+
cpu_normalization_factor = CONF.prometheus.cpu_normalization_factor
244+
245+
# Apply CPU normalization factor to energy
246+
energy_wh = energy_value * cpu_normalization_factor
239247

240-
# Calculate efficiency (simple model: actual work / max possible work)
241-
# Efficiency can be calculated as actual energy vs theoretical max
242-
# For now, use a default value
243-
efficiency = 0.5 # Placeholder
248+
# Calculate work: CpuDuration_s / Energy_wh
249+
# Avoid division by zero
250+
if energy_wh > 0:
251+
work = cpu_duration_s / energy_wh
252+
else:
253+
work = 0.0
244254

245-
# CPU normalization factor (default to 1.0 if not available)
246-
cpu_normalization_factor = 1.0
255+
# Calculate efficiency: CpuDuration_s / WallClockTime_s
256+
# Avoid division by zero
257+
if wall_clock_time_s > 0:
258+
efficiency = cpu_duration_s / wall_clock_time_s
259+
else:
260+
efficiency = 0.0
247261

248262
r = record.EnergyRecord(
249263
exec_unit_id=uuid.UUID(vm_uuid),
250264
start_exec_time=start_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
251265
end_exec_time=end_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
252-
energy_wh=energy_value,
266+
energy_wh=energy_wh,
253267
work=work,
254268
efficiency=efficiency,
255269
wall_clock_time_s=wall_clock_time_s,

caso/tests/extract/test_prometheus.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ def configured_extractor(mock_flavors):
6060
# Configure CONF
6161
CONF.set_override("site_name", "TEST-Site")
6262
CONF.set_override("service_name", "TEST-Service")
63-
CONF.set_override("prometheus_endpoint", "http://localhost:9090", group="prometheus")
63+
CONF.set_override(
64+
"prometheus_endpoint", "http://localhost:9090", group="prometheus"
65+
)
6466
CONF.set_override("prometheus_metric_name", "prometheus_value", group="prometheus")
6567
CONF.set_override("vm_uuid_label_name", "uuid", group="prometheus")
6668
CONF.set_override(
@@ -71,6 +73,7 @@ def configured_extractor(mock_flavors):
7173
CONF.set_override("prometheus_step_seconds", 30, group="prometheus")
7274
CONF.set_override("prometheus_query_range", "1h", group="prometheus")
7375
CONF.set_override("prometheus_verify_ssl", True, group="prometheus")
76+
CONF.set_override("cpu_normalization_factor", 1.0, group="prometheus")
7477

7578
with mock.patch(
7679
"caso.extract.openstack.base.BaseOpenStackExtractor.__init__",

etc/caso/caso.conf.sample

Lines changed: 26 additions & 253 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,5 @@
11
[DEFAULT]
22

3-
#
4-
# From caso
5-
#
6-
7-
# List of messengers that will dispatch records. valid values are
8-
# logstash,noop,ssm,ssmv4. You can specify more than one messenger. (list
9-
# value)
10-
#messengers = noop
11-
12-
# Spool directory. (string value)
13-
#spooldir = /var/spool/caso
14-
15-
# Directory to use for lock files. For security, the specified directory should
16-
# only be writable by the user running the processes that need locking.
17-
# Defaults to environment variable CASO_LOCK_PATH or $spooldir (string value)
18-
#lock_path = $spooldir
19-
20-
# Extract records but do not push records to SSM. This will not update the last
21-
# run date. (boolean value)
22-
#dry_run = false
23-
24-
# Site name as in GOCDB. (string value)
25-
#site_name = <None>
26-
27-
# Service name within the site (string value)
28-
#service_name = $site_name
29-
30-
# List of projects to extract accounting records from. You can use this option,
31-
# or add 'caso' tag to the project in Keystone. Please refer to the
32-
# documentation for more details. (list value)
33-
#projects =
34-
35-
# Tag used to mark a project in Keystone to be extracted by cASO (string value)
36-
#caso_tag = caso
37-
38-
# Property key used to get the VO name from the project properties. (string
39-
# value)
40-
#vo_property = VO
41-
42-
# DEPRECATED: File containing the VO <-> project mapping as used in Keystone-
43-
# VOMS. (string value)
44-
# This option is deprecated for removal.
45-
# Its value may be silently ignored in the future.
46-
# Reason: This option is marked for removal in the next release. Please see the
47-
# release notes, and migrate your current configuration to use the new project
48-
# mapping as soon as possible. If you already migrated your configuration,
49-
# please remove the JSON file to get rid of this message.
50-
#mapping_file = /etc/caso/voms.json
51-
52-
# Extract record changes until this date. If it is not set, we use now. If a
53-
# server has ended after this date, it will be included, but the consuption
54-
# reported will end on this date. If no time zone is specified, UTC will be
55-
# used. (string value)
56-
#extract_to = <None>
57-
58-
# Extract records that have changed after this date. This means that if a
59-
# record has started before this date, and it has changed after this date (i.e.
60-
# it is still running or it has ended) it will be reported.
61-
# If it is not set, extract records from last run. If it is set to None and
62-
# last run file is not present, it will extract records from the beginning of
63-
# time. If no time zone is specified, UTC will be used. (string value)
64-
#extract_from = <None>
65-
66-
# Which extractor to use for getting the data. If you do not specify anything,
67-
# nova will be used. Available choices are ['cinder', 'neutron', 'nova',
68-
# 'prometheus'] (list value)
69-
#extractor = nova,cinder,neutron
70-
713
#
724
# From oslo.config
735
#
@@ -157,12 +89,6 @@
15789
# set. (boolean value)
15890
#use_stderr = false
15991

160-
# DEPRECATED: Log output to Windows Event Log. (boolean value)
161-
# This option is deprecated for removal.
162-
# Its value may be silently ignored in the future.
163-
# Reason: Windows support is no longer maintained.
164-
#use_eventlog = false
165-
16692
# (Optional) Set the 'color' key according to log levels. This option takes
16793
# effect only when logging to stderr or stdout is used. This option is ignored
16894
# if log_config_append is set. (boolean value)
@@ -256,179 +182,6 @@
256182
#fatal_deprecations = false
257183

258184

259-
[accelerator]
260-
261-
#
262-
# From caso
263-
#
264-
265-
# Metadata key used to retrieve the accelerator type from the flavor
266-
# properties. (string value)
267-
#type_key = Accelerator:Type
268-
269-
# Metadata key used to retrieve the accelerator vendor from the flavor
270-
# properties. (string value)
271-
#vendor_key = Accelerator:Vendor
272-
273-
# Metadata key used to retrieve the accelerator model from the flavor
274-
# properties. (string value)
275-
#model_key = Accelerator:Model
276-
277-
# Metadata key used to retrieve the accelerator number from the flavor
278-
# properties. (string value)
279-
#number_key = Accelerator:Number
280-
281-
282-
[benchmark]
283-
284-
#
285-
# From caso
286-
#
287-
288-
# Metadata key used to retrieve the benchmark type from the flavor properties.
289-
# (string value)
290-
#name_key = accounting:benchmark_type
291-
292-
# Metadata key used to retrieve the benchmark value from the flavor properties.
293-
# (string value)
294-
#value_key = accounting:benchmark_value
295-
296-
297-
[keystone_auth]
298-
299-
#
300-
# From caso
301-
#
302-
303-
# Authentication type to load (string value)
304-
# Deprecated group/name - [keystone_auth]/auth_plugin
305-
#auth_type = <None>
306-
307-
# Config Section from which to load plugin specific options (string value)
308-
#auth_section = <None>
309-
310-
# PEM encoded Certificate Authority to use when verifying HTTPs connections.
311-
# (string value)
312-
#cafile = <None>
313-
314-
# PEM encoded client certificate cert file (string value)
315-
#certfile = <None>
316-
317-
# PEM encoded client certificate key file (string value)
318-
#keyfile = <None>
319-
320-
# Verify HTTPS connections. (boolean value)
321-
#insecure = false
322-
323-
# Timeout value for http requests (integer value)
324-
#timeout = <None>
325-
326-
# Collect per-API call timing information. (boolean value)
327-
#collect_timing = false
328-
329-
# Log requests to multiple loggers. (boolean value)
330-
#split_loggers = false
331-
332-
# Authentication URL (string value)
333-
#auth_url = <None>
334-
335-
# Scope for system operations (string value)
336-
#system_scope = <None>
337-
338-
# Domain ID to scope to (string value)
339-
#domain_id = <None>
340-
341-
# Domain name to scope to (string value)
342-
#domain_name = <None>
343-
344-
# Project ID to scope to (string value)
345-
# Deprecated group/name - [keystone_auth]/tenant_id
346-
#project_id = <None>
347-
348-
# Project name to scope to (string value)
349-
# Deprecated group/name - [keystone_auth]/tenant_name
350-
#project_name = <None>
351-
352-
# Domain ID containing project (string value)
353-
#project_domain_id = <None>
354-
355-
# Domain name containing project (string value)
356-
#project_domain_name = <None>
357-
358-
# ID of the trust to use as a trustee use (string value)
359-
#trust_id = <None>
360-
361-
# Optional domain ID to use with v3 and v2 parameters. It will be used for both
362-
# the user and project domain in v3 and ignored in v2 authentication. (string
363-
# value)
364-
#default_domain_id = <None>
365-
366-
# Optional domain name to use with v3 API and v2 parameters. It will be used
367-
# for both the user and project domain in v3 and ignored in v2 authentication.
368-
# (string value)
369-
#default_domain_name = <None>
370-
371-
# User id (string value)
372-
#user_id = <None>
373-
374-
# Username (string value)
375-
# Deprecated group/name - [keystone_auth]/user_name
376-
#username = <None>
377-
378-
# User's domain id (string value)
379-
#user_domain_id = <None>
380-
381-
# User's domain name (string value)
382-
#user_domain_name = <None>
383-
384-
# User's password (string value)
385-
#password = <None>
386-
387-
388-
[logstash]
389-
390-
#
391-
# From caso
392-
#
393-
394-
# Logstash host to send records to. (string value)
395-
#host = localhost
396-
397-
# Logstash server port. (integer value)
398-
#port = 5000
399-
400-
401-
[prometheus]
402-
403-
#
404-
# From caso
405-
#
406-
407-
# Prometheus server endpoint URL. (string value)
408-
#prometheus_endpoint = http://localhost:9090
409-
410-
# Name of the Prometheus metric to query for energy consumption. (string value)
411-
#prometheus_metric_name = prometheus_value
412-
413-
# Name of the label that matches the VM UUID in Prometheus metrics. (string
414-
# value)
415-
#vm_uuid_label_name = uuid
416-
417-
# List of label filters as key:value pairs to filter the Prometheus metric
418-
# (e.g., 'type_instance:scaph_process_power_microwatts'). The VM UUID label
419-
# will be added automatically based on vm_uuid_label_name. (list value)
420-
#labels = type_instance:scaph_process_power_microwatts
421-
422-
# Frequency between samples in the time series (in seconds). (integer value)
423-
#prometheus_step_seconds = 30
424-
425-
# Query time range (e.g., '1h', '6h', '24h'). (string value)
426-
#prometheus_query_range = 1h
427-
428-
# Whether to verify SSL when connecting to Prometheus. (boolean value)
429-
#prometheus_verify_ssl = true
430-
431-
432185
[sample_remote_file_source]
433186
# Example of using a remote_file source
434187
#
@@ -493,14 +246,34 @@
493246
#timeout = 60
494247

495248

496-
[ssm]
249+
[prometheus]
497250

498251
#
499-
# From caso
252+
# From caso.extract.prometheus
500253
#
501254

502-
# Directory to put the generated SSM records. (string value)
503-
#output_path = /var/spool/apel/outgoing/openstack
255+
# Prometheus server endpoint URL. (string value)
256+
#prometheus_endpoint = http://localhost:9090
257+
258+
# Name of the Prometheus metric to query for energy consumption. (string value)
259+
#prometheus_metric_name = prometheus_value
260+
261+
# Name of the label that matches the VM UUID in Prometheus metrics. (string value)
262+
#vm_uuid_label_name = uuid
263+
264+
# List of label filters as key:value pairs to filter the Prometheus metric (e.g.,
265+
# 'type_instance:scaph_process_power_microwatts'). The VM UUID label will be added
266+
# automatically based on vm_uuid_label_name. (list value)
267+
#labels = type_instance:scaph_process_power_microwatts
268+
269+
# Frequency between samples in the time series (in seconds). (integer value)
270+
#prometheus_step_seconds = 30
271+
272+
# Query time range (e.g., '1h', '6h', '24h'). (string value)
273+
#prometheus_query_range = 1h
274+
275+
# Whether to verify SSL when connecting to Prometheus. (boolean value)
276+
#prometheus_verify_ssl = true
504277

505-
# Maximum number of records to send per message (integer value)
506-
#max_size = 100
278+
# CPU normalization factor to apply to energy measurements. (floating point value)
279+
#cpu_normalization_factor = 1.0

0 commit comments

Comments
 (0)