Warning
This document is for an in-development version of Galaxy. You can alternatively view this page in the latest release if it exists or view the top of the latest release's documentation.
Source code for galaxy.job_metrics.instrumenters.cpuinfo
"""The module describes the ``cpuinfo`` job metrics plugin."""
import logging
import re
from typing import Any
from galaxy import util
from . import InstrumentPlugin
from ..formatting import (
FormattedMetric,
JobMetricFormatter,
)
log = logging.getLogger(__name__)
PROCESSOR_LINE = re.compile(r"processor\s*\:\s*(\d+)")
class CpuInfoFormatter(JobMetricFormatter):
def format(self, key: str, value: Any) -> FormattedMetric | None:
if key == "processor_count":
return FormattedMetric("Processor Count", f"{int(value)}")
else:
return super().format(key, value)
[docs]
class CpuInfoPlugin(InstrumentPlugin):
"""Gather information about processor configuration from /proc/cpuinfo.
Linux only.
"""
plugin_type = "cpuinfo"
formatter = CpuInfoFormatter()
fields: set[str] | None
[docs]
def __init__(self, **kwargs):
self.verbose = util.asbool(kwargs.get("verbose", False))
fields_str = kwargs.get("fields", None)
if isinstance(fields_str, list):
self.fields = {f.lower() for f in fields_str}
elif fields_str:
self.fields = {f.strip().lower() for f in fields_str.split(",")}
else:
self.fields = None
# Collapse a field to a single key when its value is identical across
# every processor, instead of one processor_N_<field> key per core.
self.unique = util.asbool(kwargs.get("unique", False))
[docs]
def pre_execute_instrument(self, job_directory):
return f"cat /proc/cpuinfo > '{self.__instrument_cpuinfo_path(job_directory)}'"
[docs]
def job_properties(self, job_id, job_directory):
properties: dict[str, Any] = {}
processor_count = 0
per_field_values: dict[str, dict[str, str]] = {}
with open(self.__instrument_cpuinfo_path(job_directory)) as f:
current_processor = None
for line in f:
line = line.strip().lower()
if not line: # Skip empty lines
continue
processor_line_match = PROCESSOR_LINE.match(line)
if processor_line_match:
processor_count += 1
current_processor = processor_line_match.group(1)
elif current_processor and self.verbose:
# If verbose, dump information about each processor
# into database...
field, value = line.split(":", 1)
field = field.strip()
if self.fields is not None and field not in self.fields:
continue
value = value.strip()
if self.unique:
per_field_values.setdefault(field, {})[current_processor] = value
else:
properties[f"processor_{current_processor}_{field}"] = value
if self.unique:
for field, by_processor in per_field_values.items():
distinct_values = set(by_processor.values())
reported_by_all = len(by_processor) == processor_count
if len(distinct_values) == 1 and reported_by_all:
properties[field] = distinct_values.pop()
else:
# Processors disagree, or some processors did not report this
# field at all: keep per-processor keys for it.
for processor_id, value in by_processor.items():
properties[f"processor_{processor_id}_{field}"] = value
properties["processor_count"] = processor_count
return properties
def __instrument_cpuinfo_path(self, job_directory):
return self._instrument_file_path(job_directory, "cpuinfo")
__all__ = ("CpuInfoPlugin",)