From 7bad0b7578c25a4d16e80d43697e475d2deaf1ea Mon Sep 17 00:00:00 2001 From: Robert Romero Date: Thu, 7 Aug 2025 11:45:32 -0700 Subject: [PATCH] Infer slurmdbd.conf path from slurm.conf --- README.md | 10 +++++---- src/slurmdb.py | 33 ++++++++++++++++++++++++++-- test/unit/slurmdb_validation.test.py | 20 ++++++++++++++++- 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 8d54d25..8a50e4b 100644 --- a/README.md +++ b/README.md @@ -83,12 +83,14 @@ The workflow tags the repository, builds packages, and publishes artifacts to Gi The `src/slurmdb.py` utility can connect to a running **SlurmDBD** instance and export usage metrics as JSON. Connection details are automatically scraped from -`/etc/slurm/slurmdbd.conf` (or a custom path specified via the environment -variable `SLURMDB_CONF` or the `--conf` flag). Environment variables +`slurmdbd.conf` located alongside `slurm.conf` (discovered from +`slurmctld.service` via the `ConditionPathExists` directive, defaulting to +`/etc/slurm/slurm.conf`). A custom path can be specified via the environment +variable `SLURMDB_CONF` or the `--conf` flag. Environment variables `SLURMDB_HOST`, `SLURMDB_PORT`, `SLURMDB_USER`, `SLURMDB_PASS` and `SLURMDB_DB` override any values found in the configuration file. The cluster prefix used to -select the job tables is determined from `/etc/slurm/slurm.conf` but can be set -using `SLURM_CLUSTER`, `--cluster` or `--slurm-conf`. +select the job tables is determined from the Slurm configuration file. The +setting can be overridden using `SLURM_CLUSTER`, `--cluster` or `--slurm-conf`. ```bash diff --git a/src/slurmdb.py b/src/slurmdb.py index eff78b7..06801b9 100644 --- a/src/slurmdb.py +++ b/src/slurmdb.py @@ -63,6 +63,25 @@ def _write_last_run(end_date): logging.warning("Failed to write state file %s: %s", STATE_FILE, e) +def _find_slurm_conf(service_paths=None): + """Return path to slurm.conf by inspecting slurmctld.service.""" + paths = service_paths or [ + "/usr/lib/systemd/system/slurmctld.service", + "/lib/systemd/system/slurmctld.service", + "/etc/systemd/system/slurmctld.service", + ] + for svc in paths: + try: + with open(svc) as fh: + for line in fh: + line = line.strip() + if line.startswith("ConditionPathExists=") and line.endswith("slurm.conf"): + return line.split("=", 1)[1].strip() + except OSError: + continue + return "/etc/slurm/slurm.conf" + + class SlurmDB: """Simple wrapper around the Slurm accounting database.""" @@ -77,7 +96,17 @@ def __init__( cluster=None, slurm_conf=None, ): - conf_path = config_file or os.environ.get("SLURMDB_CONF", "/etc/slurm/slurmdbd.conf") + slurm_conf_path = ( + slurm_conf + or os.environ.get("SLURM_CONF") + or _find_slurm_conf() + ) + + conf_path = ( + config_file + or os.environ.get("SLURMDB_CONF") + or os.path.join(os.path.dirname(slurm_conf_path), "slurmdbd.conf") + ) cfg = self._load_config(conf_path) self.host = host or os.environ.get("SLURMDB_HOST") or cfg.get("host", "localhost") @@ -88,7 +117,7 @@ def __init__( self._conn = None self._tres_map = None self._config_file = conf_path - self._slurm_conf = slurm_conf or os.environ.get("SLURM_CONF", "/etc/slurm/slurm.conf") + self._slurm_conf = slurm_conf_path self.cluster = ( cluster or os.environ.get("SLURM_CLUSTER") diff --git a/test/unit/slurmdb_validation.test.py b/test/unit/slurmdb_validation.test.py index 351d58e..b7f6720 100644 --- a/test/unit/slurmdb_validation.test.py +++ b/test/unit/slurmdb_validation.test.py @@ -1,7 +1,9 @@ import unittest import json +import os +import tempfile import pymysql -from slurmdb import SlurmDB +from slurmdb import SlurmDB, _find_slurm_conf from slurm_schema import extract_schema, extract_schema_from_dump class SlurmDBValidationTests(unittest.TestCase): @@ -17,6 +19,22 @@ def test_valid_config_allowed(self): db = SlurmDB(host="localhost", port=3306, user="slurm", password="", database="slurm_acct_db", cluster="cluster1") self.assertEqual(db.cluster, "cluster1") + def test_slurm_conf_from_service_file(self): + with tempfile.TemporaryDirectory() as tmp: + svc = os.path.join(tmp, "slurmctld.service") + slurm_conf = os.path.join(tmp, "slurm.conf") + with open(svc, "w") as fh: + fh.write(f"ConditionPathExists={slurm_conf}\n") + with open(slurm_conf, "w") as fh: + fh.write("ClusterName=test\n") + slurmdbd = os.path.join(tmp, "slurmdbd.conf") + with open(slurmdbd, "w") as fh: + fh.write("StorageHost=localhost\n") + path = _find_slurm_conf([svc]) + self.assertEqual(path, slurm_conf) + db = SlurmDB(slurm_conf=path) + self.assertEqual(db._config_file, slurmdbd) + def test_invalid_time_format(self): db = SlurmDB() with self.assertRaises(ValueError):