diff --git a/docs/reference/config.md b/docs/reference/config.md deleted file mode 100644 index 62a36d68..00000000 --- a/docs/reference/config.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: Config ---- - -!!! warning - This API is currently being completely reworked, and is subject to be - removed in the future when a replacement is introduced - -::: pyslurm.deprecated.config diff --git a/docs/reference/slurmctld.md b/docs/reference/slurmctld.md new file mode 100644 index 00000000..f13419fe --- /dev/null +++ b/docs/reference/slurmctld.md @@ -0,0 +1,8 @@ +--- +title: slurmctld +--- + +::: pyslurm.slurmctld + handler: python + options: + members: yes diff --git a/mkdocs.yml b/mkdocs.yml index 723b429f..c80f2d93 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -60,6 +60,8 @@ plugins: show_root_heading: true show_symbol_type_toc: true show_symbol_type_heading: true + extensions: + - scripts/griffe_exts.py:DynamicDocstrings markdown_extensions: - admonition @@ -73,6 +75,7 @@ markdown_extensions: - pymdownx.inlinehilite - pymdownx.superfences - pymdownx.details + - pymdownx.magiclink extra: version: diff --git a/pyslurm/core/job/job.pyx b/pyslurm/core/job/job.pyx index 6f4870c1..9605d0f7 100644 --- a/pyslurm/core/job/job.pyx +++ b/pyslurm/core/job/job.pyx @@ -50,6 +50,7 @@ from pyslurm.utils.helpers import ( _getpwall_to_dict, instance_to_dict, _get_exit_code, + cpu_freq_int_to_str, ) diff --git a/pyslurm/core/job/step.pyx b/pyslurm/core/job/step.pyx index 719257eb..b3a09509 100644 --- a/pyslurm/core/job/step.pyx +++ b/pyslurm/core/job/step.pyx @@ -34,8 +34,8 @@ from pyslurm.utils.helpers import ( uid_to_name, humanize_step_id, dehumanize_step_id, + cpu_freq_int_to_str, ) -from pyslurm.core.job.util import cpu_freq_int_to_str from pyslurm.utils.ctime import ( secs_to_timestr, mins_to_timestr, diff --git a/pyslurm/core/job/util.pyx b/pyslurm/core/job/util.pyx index 6014ad50..2d30a0f2 100644 --- a/pyslurm/core/job/util.pyx +++ b/pyslurm/core/job/util.pyx @@ -246,37 +246,6 @@ def cpu_freq_str_to_int(freq): raise ValueError(f"Invalid cpu freq value: {freq}.") -def cpu_freq_int_to_str(freq): - """Convert a numerical cpufreq value to its string representation.""" - if freq == slurm.CPU_FREQ_LOW: - return "LOW" - elif freq == slurm.CPU_FREQ_MEDIUM: - return "MEDIUM" - elif freq == slurm.CPU_FREQ_HIGHM1: - return "HIGHM1" - elif freq == slurm.CPU_FREQ_HIGH: - return "HIGH" - elif freq == slurm.CPU_FREQ_CONSERVATIVE: - return "CONSERVATIVE" - elif freq == slurm.CPU_FREQ_PERFORMANCE: - return "PERFORMANCE" - elif freq == slurm.CPU_FREQ_POWERSAVE: - return "POWERSAVE" - elif freq == slurm.CPU_FREQ_USERSPACE: - return "USERSPACE" - elif freq == slurm.CPU_FREQ_ONDEMAND: - return "ONDEMAND" - elif freq == slurm.CPU_FREQ_SCHEDUTIL: - return "SCHEDUTIL" - elif freq & slurm.CPU_FREQ_RANGE_FLAG: - return None - elif freq == slurm.NO_VAL or freq == 0: - return None - else: - # This is in kHz - return freq - - def dependency_str_to_dict(dep): if not dep: return None diff --git a/pyslurm/core/partition.pyx b/pyslurm/core/partition.pyx index 9c50390b..53e41648 100644 --- a/pyslurm/core/partition.pyx +++ b/pyslurm/core/partition.pyx @@ -2,7 +2,7 @@ # partition.pyx - interface to work with partitions in slurm ######################################################################### # Copyright (C) 2023 Toni Harzendorf -# Copyright (C) 2023 PySlurm Developers +# Copyright (C) 2025 PySlurm Developers # # This file is part of PySlurm # @@ -31,6 +31,7 @@ from pyslurm.core.error import RPCError, verify_rpc from pyslurm.utils.ctime import timestamp_to_date, _raw_time from pyslurm.constants import UNLIMITED from pyslurm.settings import LOCAL_CLUSTER +from pyslurm.core.slurmctld.config import _get_memory from pyslurm import xcollections from pyslurm.utils.helpers import ( uid_to_name, @@ -832,21 +833,3 @@ cdef _concat_job_default_str(typ, val, char **job_defaults_str): current.update({typ : _val}) cstr.from_dict(job_defaults_str, current) - - -def _get_memory(value, per_cpu): - if value != slurm.NO_VAL64: - if value & slurm.MEM_PER_CPU and per_cpu: - if value == slurm.MEM_PER_CPU: - return UNLIMITED - return u64_parse(value & (~slurm.MEM_PER_CPU)) - - # For these values, Slurm interprets 0 as being equal to - # INFINITE/UNLIMITED - elif value == 0 and not per_cpu: - return UNLIMITED - - elif not value & slurm.MEM_PER_CPU and not per_cpu: - return u64_parse(value) - - return None diff --git a/pyslurm/core/slurmctld.pyx b/pyslurm/core/slurmctld.pyx deleted file mode 100644 index 7f06966e..00000000 --- a/pyslurm/core/slurmctld.pyx +++ /dev/null @@ -1,62 +0,0 @@ -######################################################################### -# slurmctld.pyx - pyslurm slurmctld api -######################################################################### -# Copyright (C) 2023 Toni Harzendorf -# -# This file is part of PySlurm -# -# PySlurm is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. - -# PySlurm is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with PySlurm; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# cython: c_string_type=unicode, c_string_encoding=default -# cython: language_level=3 - -from pyslurm.core.error import verify_rpc, RPCError - - -cdef class Config: - - def __cinit__(self): - self.ptr = NULL - - def __init__(self, job_id): - raise RuntimeError("Cannot instantiate class directly") - - def __dealloc__(self): - slurm_free_ctl_conf(self.ptr) - self.ptr = NULL - - @staticmethod - def load(): - cdef Config conf = Config.__new__(Config) - verify_rpc(slurm_load_ctl_conf(0, &conf.ptr)) - return conf - - @property - def cluster(self): - return cstr.to_unicode(self.ptr.cluster_name) - - @property - def preempt_mode(self): - cdef char *tmp = slurm_preempt_mode_string(self.ptr.preempt_mode) - return cstr.to_unicode(tmp) - - @property - def suspend_program(self): - return cstr.to_unicode(self.ptr.suspend_program) - - @property - def resume_program(self): - return cstr.to_unicode(self.ptr.resume_program) - diff --git a/pyslurm/core/slurmctld/__init__.pxd b/pyslurm/core/slurmctld/__init__.pxd new file mode 100644 index 00000000..edc6a267 --- /dev/null +++ b/pyslurm/core/slurmctld/__init__.pxd @@ -0,0 +1,4 @@ +# cython: c_string_type=unicode, c_string_encoding=default +# cython: language_level=3 + +from .config cimport Config diff --git a/pyslurm/core/slurmctld/__init__.py b/pyslurm/core/slurmctld/__init__.py new file mode 100644 index 00000000..fe8f970b --- /dev/null +++ b/pyslurm/core/slurmctld/__init__.py @@ -0,0 +1,27 @@ +from .config import ( + Config, + MPIConfig, + AccountingGatherConfig, + CgroupConfig, +) +from .enums import ShutdownMode +from .base import ( + PingResponse, + ping, + ping_primary, + ping_backup, + ping_all, + shutdown, + reconfigure, + takeover, + add_debug_flags, + remove_debug_flags, + clear_debug_flags, + get_debug_flags, + set_log_level, + get_log_level, + enable_scheduler_logging, + is_scheduler_logging_enabled, + set_fair_share_dampening_factor, + get_fair_share_dampening_factor, +) diff --git a/pyslurm/core/slurmctld/base.pxd b/pyslurm/core/slurmctld/base.pxd new file mode 100644 index 00000000..fd6f6a4d --- /dev/null +++ b/pyslurm/core/slurmctld/base.pxd @@ -0,0 +1,63 @@ +######################################################################### +# slurmctld/base.pxd - pyslurm slurmctld api functions +######################################################################### +# Copyright (C) 2025 Toni Harzendorf +# +# This file is part of PySlurm +# +# PySlurm is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# PySlurm is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with PySlurm; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# cython: c_string_type=unicode, c_string_encoding=default +# cython: language_level=3 + +from pyslurm cimport slurm +from pyslurm.slurm cimport ( + slurm_conf_t, + slurm_reconfigure, + slurm_shutdown, + slurm_ping, + slurm_takeover, + slurm_set_debugflags, + slurm_set_debug_level, + slurm_set_schedlog_level, + slurm_set_fs_dampeningfactor, +) +from libc.stdint cimport uint16_t, uint64_t +from pyslurm.utils.uint cimport u16_parse +from pyslurm.utils cimport cstr + + +cdef class PingResponse: + """Slurm Controller Ping response information + + Attributes: + is_primary (bool): + Whether this Slurm Controller is the primary Server. + is_responding (bool): + Whether this Slurm Controller actually responds to the ping. + index (int): + The index in the slurm.conf. For example, 0 means primary. + hostname (str): + Hostname of the Controller + latency (float): + The latency which the Controller responds with. This is in + milliseconds. + """ + cdef public: + is_primary + is_responding + index + hostname + latency diff --git a/pyslurm/core/slurmctld/base.pyx b/pyslurm/core/slurmctld/base.pyx new file mode 100644 index 00000000..efdd14d3 --- /dev/null +++ b/pyslurm/core/slurmctld/base.pyx @@ -0,0 +1,410 @@ +######################################################################### +# slurmctld/base.pyx - pyslurm slurmctld api functions +######################################################################### +# Copyright (C) 2025 Toni Harzendorf +# +# This file is part of PySlurm +# +# PySlurm is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# PySlurm is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with PySlurm; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# cython: c_string_type=unicode, c_string_encoding=default +# cython: language_level=3 + +from pyslurm.core.error import verify_rpc, RPCError +from pyslurm.utils.helpers import instance_to_dict +from pyslurm.utils import cstr +from pyslurm.utils.uint import u16_parse +from typing import Union +import time +from enum import IntEnum +from .config import Config +from .enums import ShutdownMode + + +cdef class PingResponse: + + def to_dict(self): + """Slurmctld ping response formatted as dictionary. + + Returns: + (dict): Ping response as a dict + + Examples: + >>> from pyslurm import slurmctld + >>> ctld_primary = slurmctld.Config.ping(0) + >>> primary_dict = ctld_primary.to_dict() + """ + return instance_to_dict(self) + + +def ping(index): + """Ping a Slurm controller + + Returns: + (pyslurm.slurmctld.PingResponse): a ping response + + Examples: + >>> from pyslurm import slurmctld + >>> resp = slurmctld.ping(0) + >>> print(resp.hostname, resp.latency) + slurmctl 1.246 + """ + t0 = time.perf_counter() + rc = slurm_ping(index) + t1 = time.perf_counter() + + verify_rpc(rc) + ctl_cnt = slurm.slurm_conf.control_cnt + + if index >= ctl_cnt: + raise RPCError(msg="Invalid Index specified.") + + info = PingResponse() + info.is_primary = index == 0 + info.is_responding = not rc + info.index = index + info.hostname = cstr.to_unicode(slurm.slurm_conf.control_machine[index]) + info.latency = round((t1 - t0) * 1000, 3) + + return info + + +def ping_primary(): + """Ping the primary Slurm Controller. + + See `ping()` for more information and examples. + + Returns: + (pyslurm.slurmctld.PingResponse): a ping response + + Examples: + >>> from pyslurm import slurmctld + >>> resp = slurmctld.ping_primary() + >>> print(resp.hostname, resp.latency, resp.is_primary) + slurmctl 1.222 True + """ + return ping(0) + + +def ping_backup(): + """Ping the first backup Slurm Controller. + + See `ping()` for more information and examples. + + Returns: + (pyslurm.slurmctld.PingResponse): a ping response + + Examples: + >>> from pyslurm import slurmctld + >>> resp = slurmctld.ping_backup() + >>> print(resp.hostname, resp.latency, resp.is_primary) + slurmctlbackup 1.373 False + """ + return ping(1) + + +def ping_all(): + """Ping all Slurm Controllers. + + Returns: + (list[pyslurm.slurmctld.PingResponse]): a list of ping responses + + Raises: + (pyslurm.RPCError): When the ping was not successful. + + Examples: + >>> from pyslurm import slurmctld + >>> resps = slurmctld.ping_all() + >>> for resp in resps: + ... print(resp.hostname, resp.latency) + ... + slurmctl 1.246 + slurmctlbackup 1.373 + """ + cdef list out = [] + + ctl_cnt = slurm.slurm_conf.control_cnt + for i in range(ctl_cnt): + out.append(ping(i)) + + return out + + +def shutdown(mode: Union[ShutdownMode, int]): + """Shutdown Slurm Controller or all Daemons + + Args: + mode: + Whether only the Slurm controller shut be downed, or also all other + slurmd daemons. + + Raises: + (pyslurm.RPCError): When shutdowning the daemons was not successful. + + Examples: + >>> from pyslurm import slurmctld + >>> slurmctld.shutdown(slurmctld.ShutdownMode.ALL) + """ + verify_rpc(slurm_shutdown(int(mode))) + + +def reconfigure(): + """Trigger Slurm Controller to reload the Config + + Raises: + (pyslurm.RPCError): When reconfiguring was not successful. + + Examples: + >>> from pyslurm import slurmctld + >>> slurmctld.reconfigure() + """ + verify_rpc(slurm_reconfigure()) + + +def takeover(index = 1): + """Let a Backup Slurm Controller take over as the Primary. + + Args: + index (int, optional=1): + Index of the Backup Controller that should take over. By default, + the `index` is `1`, meaning the next Controller configured after + the Primary in `slurm.conf` (second `SlurmctldHost` entry) will be + asked to take over operation. + + If you have more than one backup controller configured, you can for + example also pass `2` as the index. + + Raises: + (pyslurm.RPCError): When reconfiguring was not successful. + + Examples: + >>> from pyslurm import slurmctld + >>> slurmctld.takeover(1) + """ + verify_rpc(slurm_takeover(index)) + + +def add_debug_flags(flags): + """Add DebugFlags to `slurmctld` + + Args: + flags (list[str]): + For an available list of possible values, please check the + `slurm.conf` documentation under `DebugFlags`. + + Raises: + (pyslurm.RPCError): When setting the debug flags was not successful. + + Examples: + >>> from pyslurm import slurmctld + >>> slurmctld.add_debug_flags(["CpuFrequency", "Backfill"]) + """ + if not flags: + return + + data = _debug_flags_str_to_int(flags) + if not data: + raise RPCError(msg="Invalid Debug Flags specified.") + + verify_rpc(slurm_set_debugflags(data, 0)) + + +def remove_debug_flags(flags): + """Remove DebugFlags from `slurmctld`. + + Args: + flags (list[str]): + For an available list of possible values, please check the + `slurm.conf` documentation under `DebugFlags`. + + Raises: + (pyslurm.RPCError): When removing the debug flags was not successful. + + Examples: + >>> from pyslurm import slurmctld + >>> slurmctld.remove_debug_flags(["CpuFrequency"]) + """ + if not flags: + return + + data = _debug_flags_str_to_int(flags) + if not data: + raise RPCError(msg="Invalid Debug Flags specified.") + + verify_rpc(slurm_set_debugflags(0, data)) + + +def clear_debug_flags(): + """Remove all currently set debug flags from `slurmctld`. + + Raises: + (pyslurm.RPCError): When removing the debug flags was not successful. + + Examples: + >>> from pyslurm import slurmctld + >>> slurmctld.clear_debug_flags() + >>> print(slurmctld.get_debug_flags()) + [] + """ + current_flags = get_debug_flags() + if not current_flags: + return + + data = _debug_flags_str_to_int(current_flags) + verify_rpc(slurm_set_debugflags(0, data)) + + +def get_debug_flags(): + """Get the current list of debug flags for the `slurmctld`. + + Raises: + (pyslurm.RPCError): When getting the debug flags was not successful. + + Examples: + >>> from pyslurm import slurmctld + >>> flags = slurmctld.get_debug_flags() + >>> print(flags) + ['CpuFrequency', 'Backfill'] + """ + return Config.load().debug_flags + + +def set_log_level(level): + """Set the logging level for `slurmctld`. + + Args: + level (str): + For an available list of possible values, please check the + `slurm.conf` documentation under `SlurmctldDebug`. + + Raises: + (pyslurm.RPCError): When setting the log level was not successful. + + Examples: + >>> from pyslurm import slurmctld + >>> slurmctld.set_log_level("quiet") + >>> log_level = slurmctld.get_log_level() + >>> print(log_level) + quiet + """ + data = _log_level_str_to_int(level) + verify_rpc(slurm_set_debug_level(data)) + + +def get_log_level(): + """Get the current log level for the `slurmctld`. + + Raises: + (pyslurm.RPCError): When getting the log level was not successful. + + Examples: + >>> from pyslurm import slurmctld + >>> log_level = slurmctld.get_log_level() + >>> print(log_level) + quiet + """ + return Config.load().slurmctld_log_level + + +def enable_scheduler_logging(): + """Enable scheduler logging for `slurmctld`. + + Raises: + (pyslurm.RPCError): When enabling scheduler logging was not successful. + + Examples: + >>> from pyslurm import slurmctld + >>> slurmctld.enable_scheduler_logging() + >>> print(slurmctld.is_scheduler_logging_enabled()) + True + """ + verify_rpc(slurm_set_schedlog_level(1)) + + +def is_scheduler_logging_enabled(): + """Check whether scheduler logging is enabled for `slurmctld`. + + Returns: + (bool): Whether scheduler logging is enabled or not. + + Raises: + (pyslurm.RPCError): When getting the scheduler logging was not + successful. + + Examples: + >>> from pyslurm import slurmctld + >>> print(slurmctld.is_scheduler_logging_enabled()) + False + """ + return Config.load().scheduler_logging_enabled + + +def set_fair_share_dampening_factor(factor): + """Set the FairShare Dampening factor. + + Args: + factor (int): + The factor to set. A minimum value of `1`, and a maximum value of + `65535` are allowed. + + Raises: + (pyslurm.RPCError): When setting the factor was not successful. + + Examples: + >>> from pyslurm import slurmctld + >>> slurmctld.set_fair_share_dampening_factor(100) + >>> print(slurmctld.get_fair_share_dampening_factor) + 100 + """ + max_value = (2 ** 16) - 1 + if not factor or factor >= max_value: + raise RPCError(msg=f"Invalid Dampening factor: {factor}. " + f"Factor must be between 0 and {max_value}.") + + verify_rpc(slurm_set_fs_dampeningfactor(factor)) + + +def get_fair_share_dampening_factor(): + """Get the currently set FairShare Dampening factor. + + Raises: + (pyslurm.RPCError): When getting the factor was not successful. + + Examples: + >>> from pyslurm import slurmctld + >>> factor = slurmctld.get_fair_share_dampening_factor() + >>> print(factor) + 100 + """ + return Config.load().fair_share_dampening_factor + + +def _debug_flags_str_to_int(flags): + cdef: + uint64_t flags_num = 0 + char *flags_str = NULL + + flags_str = cstr.from_unicode(cstr.list_to_str(flags)) + slurm.debug_str2flags(flags_str, &flags_num) + return flags_num + + +def _log_level_str_to_int(level): + cdef uint16_t data = slurm.log_string2num(str(level)) + if u16_parse(data, zero_is_noval=False) is None: + raise RPCError(msg=f"Invalid Log level: {level}.") + + return data + diff --git a/pyslurm/core/slurmctld/config.pxd b/pyslurm/core/slurmctld/config.pxd new file mode 100644 index 00000000..75efd735 --- /dev/null +++ b/pyslurm/core/slurmctld/config.pxd @@ -0,0 +1,1405 @@ +######################################################################### +# slurmctld/config.pxd - pyslurm slurmctld config api +######################################################################### +# Copyright (C) 2025 Toni Harzendorf +# +# Note: Some classes are annotated with additional Copyright notices further +# down +# +# This file is part of PySlurm +# +# PySlurm is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# PySlurm is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with PySlurm; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# cython: c_string_type=unicode, c_string_encoding=default +# cython: language_level=3 + +from pyslurm cimport slurm +from pyslurm.slurm cimport ( + slurm_conf_t, + slurm_load_ctl_conf, + slurm_free_ctl_conf, + slurm_preempt_mode_string, + slurm_accounting_enforce_string, + slurm_sprint_cpu_bind_type, + slurm_ctl_conf_2_key_pairs, + cpu_bind_type_t, + list_t, + xfree, +) +from pyslurm.utils cimport cstr +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, int64_t +from pyslurm.utils.uint cimport ( + u16_parse, + u32_parse, + u64_parse, + u16_parse_bool, +) + +from pyslurm.db.util cimport ( + SlurmList, + SlurmListItem, +) + + +cdef dict _parse_config_key_pairs(void *ptr, owned=*) + + +ctypedef struct config_key_pair_t: + char *name + char *value + + +# Documentation for the attributes in the Config class have been largely taken +# from the official slurm.conf overview at: +# https://slurm.schedmd.com/slurm.conf.html +# +# Therefore, the following Copyright notices that slurm.conf has (see +# https://slurm.schedmd.com/slurm.conf.html#SECTION_COPYING), are also listed +# here: +# +# Copyright (C) 2002-2007 The Regents of the University of California. Produced +# at Lawrence Livermore National Laboratory (cf, pyslurm/slurm/SLURM_DISCLAIMER). +# Copyright (C) 2008-2010 Lawrence Livermore National Security. +# Copyright (C) 2010-2022 SchedMD LLC. +cdef class Config: + """The Slurm Configuration. + + All attributes in this class are read-only. + + Attributes: + cgroup_config (pyslurm.slurmctld.CgroupConfig): + The CGroup Configuration data + accounting_gather_config (pyslurm.slurmctld.AccountingGatherConfig): + The Accounting Gather Configuration data. + mpi_config (pyslurm.slurmctld.MPIConfig): + The MPI Configuration data. + accounting_storage_enforce (list[str]): + List of enforcements on Job submissions. + + {slurm.conf#OPT_AccountingStorageEnforce} + accounting_storage_backup_host (str): + Name of the backup machine hosting the Slurm database. + + {slurm.conf#OPT_AccountingStorageBackupHost} + accounting_storage_external_hosts (list[str]): + List of external slurmdbds to register with. + + {slurm.conf#OPT_AccountingStorageExternalHost} + accounting_storage_host (str): + Name of the machine hosting the slurm database. + {slurm.conf#OPT_AccountingStorageHost} + + accounting_storage_parameters (dict[str, Union[str, int, bool]]): + Options for the accounting storage Plugin + + If a value in this dict is `True`, it means this parameter does not + have any additional options specified, and is just an "enabled" + option. + + {slurm.conf#OPT_AccountingStorageParameters} + accounting_storage_port (int): + Listening port of the Accounting Database Server + + {slurm.conf#OPT_AccountingStoragePort} + accounting_storage_tres (list): + List of configured Resources to track on the Cluster. + + {slurm.conf#OPT_AccountingStorageTRES} + accounting_storage_type (str): + The accounting storage type used. + + {slurm.conf#OPT_AccountingStorageType} + accounting_storage_user (str): + The User accounting accessing the accounting database. + + {slurm.conf#OPT_AccountingStorageUser} + accounting_store_flags (list[str]): + List of fields that the slurmctld also sends to the accounting + database. + + {slurm.conf#OPT_AccountingStoreFlags} + accounting_gather_node_frequency (int): + Accounting-Gather plugins sampling interval for node accounting. + + {slurm.conf#OPT_AcctGatherNodeFreq} + accounting_gather_energy_type (str): + Plugin used for energy consumption accounting. + + {slurm.conf#OPT_AcctGatherEnergyType} + accounting_gather_interconnect_type (str): + Plugin used for interconnect network traffic accounting. + + {slurm.conf#OPT_AcctGatherInterconnectType} + accounting_gather_filesystem_type (str): + Plugin used for filesystem traffic accounting. + + {slurm.conf#OPT_AcctGatherFilesystemType} + accounting_gather_profile_type (str): + Plugin used for detailed job profiling. + + {slurm.conf#OPT_AcctGatherProfileType} + allow_spec_resource_usage (bool): + Whether Slurm allows jobs to override the nodes configured + `CoreSpecCount` + + {slurm.conf#OPT_AllowSpecResourcesUsage} + auth_alt_types (list[str]): + List of alternative authentication plugins the slurmctld permits. + + {slurm.conf#OPT_AuthAltTypes} + auth_alt_parameters (dict[str, Union[str, int, bool]]): + Options for the alternative authentication plugins. + + If a value in this dict is `True`, it means this parameter does not + have any additional options specified, and is just an "enabled" + option. + + {slurm.conf#OPT_AuthAltParameters} + auth_info (list[str]): + List of additional information used for authentication of + communication between Slurm daemons. + + {slurm.conf#OPT_AuthInfo} + auth_type (str): + Primary authentication method for communications between Slurm + components. + + {slurm.conf#OPT_AuthType} + batch_start_timeout (int): + The maximum time (in seconds) that a batch job is permitted for + launching before being considered missing and releasing the + allocation. + + {slurm.conf#OPT_BatchStartTimeout} + bcast_exclude_paths (list[str]): + List of absolute directory paths to be excluded when + autodetecting and broadcasting executable shared object dependencies + through `sbcast` or `srun --bcast`. + + {slurm.conf#OPT_BcastExclude} + bcast_parameters (dict[str, Union[int, str, bool]]: + Options for `sbcast` and `srun --bcast` behaviour. + + If a value in this dict is `True`, it means this parameter does not + have any additional options specified, and is just an "enabled" + option. + + {slurm.conf#OPT_BcastParameters} + burst_buffer_type (str): + Plugin used to manage burst buffers. + + {slurm.conf#OPT_BurstBufferType} + slurmctld_boot_time (int): + Timestamp of when the slurmctld last booted. + certmgr_parameters (str): + List of options for the certmgr Plugin. + certmgr_type (str): + Plugin used for certmgr mechanism. + cli_filter_plugins (list[str]): + List of CLI Filter plugins to use. + + {slurm.conf#OPT_CliFilterPlugins} + cluster_name (str): + Name of the Cluster. + + {slurm.conf#OPT_ClusterName} + communication_parameters (dict[str, Union[str, int]]): + Communication options for Cluster daemons. + + If a value in this dict is `True`, it means this parameter does not + have any additional options specified, and is just an "enabled" + option. + + {slurm.conf#OPT_CommunicationParameters} + complete_wait_time (int): + The time to wait, in seconds, when any job is in the COMPLETING state + before any additional jobs are scheduled. + + {slurm.conf#OPT_CompleteWait} + default_cpu_frequency_governor (str): + Default CPU governor to use when a Job has not specified the + `--cpu-freq` option. + + {slurm.conf#OPT_CpuFreqDef} + cpu_frequency_governors (list[str]): + List of CPU Governors allowed to be set on Job submission. + + {slurm.conf#OPT_CpuFreqGovernors} + credential_type (str): + Cryptographic signature tool to be used when creating job step + credentials. + + {slurm.conf#OPT_CredType} + data_parser_parameters (str): + Default value to apply for `data_parser` plugin parameters. + + {slurm.conf#OPT_DataParserParameters} + debug_flags (list[str]): + List of DebugFlags currently set for Daemons. + + {slurm.conf#OPT_DebugFlags} + default_memory_per_cpu (int): + Default real memory size available per allocated CPU in Mebibytes. + + {slurm.conf#OPT_DefMemPerCPU} + default_memory_per_node (int): + Default real memory size available per allocated Node in Mebibytes. + + {slurm.conf#OPT_DefMemPerNode} + dependency_parameters (dict[str, Union[str, int, bool]]): + List of parameters for dependencies. + + If a value in this dict is `True`, it means this parameter does not + have any additional options specified, and is just an "enabled" + option. + + {slurm.conf#OPT_DependencyParameters} + disable_root_jobs (bool): + Whether root can submit Jobs or not. + + {slurm.conf#OPT_DisableRootJobs} + eio_timeout (int): + The number of seconds srun waits for slurmstepd to close the TCP/IP + connection used to relay data between the user application and srun + when the user application terminates. + + {slurm.conf#OPT_EioTimeout} + enforce_partition_limits (str): + Controls which Limits are enforced on Partition level. + + {slurm.conf#OPT_EnforcePartLimits} + epilog (list[str]): + List of Epilog scripts in use that are executed as root on every + node when a Job completes. + + {slurm.conf#OPT_Epilog} + epilog_msg_time (int): + The number of microseconds that the slurmctld daemon requires to + process an epilog completion message from the slurmd daemons. + + {slurm.conf#OPT_EpilogMsgTime} + epilog_slurmctld (list[str]): + List of Epilog scripts in use that are executed by slurmctld at job + allocation. + + {slurm.conf#OPT_EpilogSlurmctld} + fair_share_dampening_factor (int): + Dampen the effect of exceeding a user or group's fair share of + allocated resources. + + {slurm.conf#OPT_FairShareDampeningFactor} + federation_parameters (dict[str, Union[str, int, bool]]): + Options for Federations + + If a value in this dict is `True`, it means this parameter does not + have any additional options specified, and is just an "enabled" + option. + + {slurm.conf#OPT_FederationParameters} + first_job_id (int): + The job id to be used for the first job submitted. + + {slurm.conf#OPT_FirstJobId} + get_environment_timeout (int): + How long a Job waits (in seconds) to load the Users environment + before attempting to load it from a cache file. + + {slurm.conf#OPT_GetEnvTimeout} + gres_types (list[str]): + List of generic resources to be managed. + + {slurm.conf#OPT_GresTypes} + group_update_force (bool): + Whether user group membership information is updated periodically, + even if there are no changes to `/etc/group`. + + {slurm.conf#OPT_GroupUpdateForce} + group_update_time (int): + How frequently information about user group membership is updated, + and how longer it is cached (in seconds). + + {slurm.conf#OPT_GroupUpdateTime} + default_gpu_frequency (str): + Default GPU frequency to use when running a job step if it has not + been explicitly set using the --gpu-freq option. + + {slurm.conf#OPT_GpuFreqDef} + hash_plugin (str): + Type of hash plugin used for network communication. + + {slurm.conf#OPT_HashPlugin} + hash_value (str): + Current configuration hash value (hex). + health_check_interval (int): + Interval in seconds between executions of `HealthCheckProgram` + + {slurm.conf#OPT_HealthCheckInterval} + health_check_node_state (list[str]): + List of node states which are eligible to execute + `HealthCheckProgram`. + + {slurm.conf#OPT_HealthCheckNodeState} + health_check_program (str): + Pathname of a script that is periodally executed as root user on + all compute nodes. + + {slurm.conf#OPT_HealthCheckProgram} + inactive_limit (int): + The interval, in seconds, after which a non-responsive job + allocation command (e.g. `srun` or `salloc`) will result in the job + being terminated. + + {slurm.conf#OPT_InactiveLimit} + interactive_step_options (str): + When `LaunchParameters=use_interactive_step` is enabled, launching + salloc will automatically start an srun process with + `interactive_step_options` to launch a terminal on a node in the job + allocation. + + {slurm.conf#OPT_InteractiveStepOptions} + job_accounting_gather_type (str): + The job accounting gather plugin used to collect usage information + about Jobs. + + {slurm.conf#OPT_JobAcctGatherType} + job_accounting_gather_frequency (dict[str, int]): + The job accounting and profiling sampling intervals. + + {slurm.conf#OPT_JobAcctGatherFrequency} + job_accounting_gather_parameters (list[str]): + Arbitrary paramerers for `job_accounting_gather_type` + + {slurm.conf#OPT_JobAcctGatherParams} + job_completion_host (str): + Name of the machine hosting the job completion database. + + {slurm.conf#OPT_JobCompHost} + job_completion_location (str): + Sets a string which has different meaning depending on + `job_completion_type` + + {slurm.conf#OPT_JobCompLoc} + job_completion_parameters (dict[str, Union[str, int, bool]]): + Arbitrary text passed to the Job completion plugin. + + If a value in this dict is `True`, it means this parameter does not + have any additional options specified, and is just an "enabled" + option. + + {slurm.conf#OPT_JobCompParams} + job_completion_port (int): + The listening port of the job completion database server. + + {slurm.conf#OPT_JobCompPort} + job_completion_type (str): + Job completion logging mechanism type + + {slurm.conf#OPT_JobCompType} + job_completion_user (str): + User account user for accessing the job completion database. + + {slurm.conf#OPT_JobCompUser} + job_container_type (str): + Plugin used for job isolation through Linux namespaces. + + {slurm.conf#OPT_JobContainerType} + job_file_append (bool): + This option controls what to do if a job's output or error file + exist when the job is started. If `True`, then append to the + existing file. `False`, which is the default, means any existing + files are truncated. + + {slurm.conf#OPT_JobFileAppend} + job_requeue (bool): + Whether jobs are requeuable by default + + {slurm.conf#OPT_JobRequeue} + job_submit_plugins (list[str]): + Site specific list of plugins used for setting default job + parameters and/or logging events + + {slurm.conf#OPT_JobSubmitPlugins} + kill_on_bad_exit (bool): + Whether a step will be terminated immediately if any task is + crashed or aborted. + + {slurm.conf#OPT_KillOnBadExit} + kill_wait_time (int): + The interval, in seconds, given to a job's processes between the + `SIGTERM` and `SIGKILL` signals upon reaching its time limit. + + {slurm.conf#OPT_KillWait} + launch_parameters (list[str]): + Options for the job launch plugin. + + {slurm.conf#OPT_LaunchParameters} + licenses (dict[str, int]): + Licenses that can be allocated to jobs. + + {slurm.conf#OPT_Licenses} + log_time_format (str): + Format of the timestamp in slurmctld and slurmd log-files. + + {slurm.conf#OPT_LogTimeFormat} + mail_domain (str): + Domain name to qualify usernames if email address is not explicitly + given with the `--mail-user` option. + + {slurm.conf#OPT_MailDomain} + mail_program (str): + Pathname to the program used to send emails per user request + + {slurm.conf#OPT_MailProg} + max_array_size (int): + Maximum job array task index value allowed. + + {slurm.conf#OPT_MaxArraySize} + max_batch_requeue (int): + Maximum number of times a batch job may be automatically requeued + before being marked as `JobHeldAdmin`. + + {slurm.conf#OPT_MaxBatchRequeue} + max_dbd_msgs (int): + Maximum number of messages the Slurm controllers queues before + starting to drop them when the slurmdbd is down. + + {slurm.conf#OPT_MaxDBDMsgs} + max_job_count (int): + Maximum number of jobs slurmctld can have in memory at one time. + + {slurm.conf#OPT_MaxJobCount} + max_job_id (int): + Highest job ID possible for Jobs that will be assigned + automatically on submission. + + {slurm.conf#OPT_MaxJobId} + max_memory_per_cpu (int): + Maximum real memory size available per allocated CPU in Mebibytes. + + {slurm.conf#OPT_MaxMemPerCPU} + max_memory_per_node (int): + Maximum real memory size available per allocated Node in Mebibytes. + + {slurm.conf#OPT_MaxMemPerNode} + max_node_count (int): + Maximum count of nodes which may exist in the slurmctld. + + {slurm.conf#OPT_MaxNodeCount} + max_step_count (int): + Maximum number of Steps that any Job can initiate. + + {slurm.conf#OPT_MaxStepCount} + max_tasks_per_node (int): + Maximum number of tasks Slurm will allow for a job step to spawn on + a single node. + + {slurm.conf#OPT_MaxTasksPerNode} + mcs_plugin (str): + Associate a security label to jobs, for resource sharing among jobs + with the same label. + + {slurm.conf#OPT_MCSPlugin} + mcs_parameters (list[str]): + Parameters for the MCS Plugin. + + {slurm.conf#OPT_MCSParameters} + min_job_age (int): + Minimum age (in seconds) of a completed Job before its record is + cleared from slurmctlds memory. + + {slurm.conf#OPT_MinJobAge} + mpi_default (str): + Default type of MPI that will be used. + + {slurm.conf#OPT_MpiDefault} + mpi_parameters (dict[str, Union[str, int, bool]]): + Parameters for MPI. + + If a value in this dict is `True`, it means this parameter does not + have any additional options specified, and is just an "enabled" + option. + + {slurm.conf#OPT_MpiParams} + message_timeout (int): + Time permitted for a round-trip communication to complete in + seconds. + + {slurm.conf#OPT_MessageTimeout} + next_job_id (int): + Next Job-ID that will be assigned. + node_features_plugins (list[str]): + Plugins to be used for support of node features which can change + through time. + + {slurm.conf#OPT_NodeFeaturesPlugins} + over_time_limit (int): + Number of minutes by which a job can exceed its time limit before + being canceled. + + {slurm.conf#OPT_OverTimeLimit} + plugin_dirs (list[str]): + List of paths where Slurm looks for plugins. + + {slurm.conf#OPT_PluginDir} + plugin_stack_config (str): + Location of the config file for Slurm stackable plugins. + + {slurm.conf#OPT_PlugStackConfig} + preempt_exempt_time (int): + Minimum run time for all jobs before they can be considered for + preemption. + + {slurm.conf#OPT_PreemptExemptTime} + preempt_mode (str): + Mechanism used to preempt jobs or enable gang scheduling. + + {slurm.conf#OPT_PreemptMode} + preempt_parameters (dict[str, Union[str, int, bool]]): + Options for the Preempt Plugin. + + If a value in this dict is `True`, it means this parameter does not + have any additional options specified, and is just an "enabled" + option. + + {slurm.conf#OPT_PreemptParameters} + preempt_type (str): + Plugin used to identify which jobs can be preempted. + + {slurm.conf#OPT_PreemptMode} + prep_parameters (list[str]): + Parameters passed to the PrEpPlugins. + + {slurm.conf#OPT_PrEpParamrters} + prep_plugins (list[str]): + List of PrEp Plugins to be used. + + {slurm.conf#OPT_PrEpPlugins} + priority_decay_half_life (int): + Controls how long (in seconds) prior resource use is considered in + determining how over- or under-serviced an association is. + + {slurm.conf#OPT_PriorityDecayHalfLife} + priority_calc_period (int): + Period (in minutes) in which the half-life decay will be + re-calculated. + + {slurm.conf#OPT_PriorityCalcPeriod} + priority_favor_small (bool): + Whether small jobs should be given preferential scheduling + priority. + + {slurm.conf#OPT_PriorityFavorSmall} + priority_flags (list[str]): + List of flags that modify priority behaviour. + + {slurm.conf#OPT_PriorityFlags} + priority_max_age (int): + Job age (in seconds) that is needed before receiving the maximum + age factor in computing priority. + + {slurm.conf#OPT_PriorityMaxAge} + priority_parameters (str): + Arbitrary string used by the `priority_type` plugin. + + {slurm.conf#OPT_PriorityParameters} + priority_usage_reset_period (str): + At this interval the usage of associations will be reset to 0. + + {slurm.conf#OPT_PriorityUsageResetPeriod} + priority_type (str): + Specifies the plugin to be used in establishing a job's scheduling + priority. + + {slurm.conf#OPT_PriorityType} + priority_weight_age (int): + An integer value that sets the degree to which the queue wait time + component contributes to the job's priority. + + {slurm.conf#OPT_PriorityWeightAge} + priority_weight_assoc (int): + An integer value that sets the degree to which the association + component contributes to the job's priority. + + {slurm.conf#OPT_PriorityWeightAssoc} + priority_weight_fair_share (int): + An integer value that sets the degree to which the fair-share + component contributes to the job's priority. + + {slurm.conf#OPT_PriorityWeightFairShare} + priority_weight_job_size (int): + An integer value that sets the degree to which the job size + component contributes to the job's priority. + + {slurm.conf#OPT_PriorityWeightJobSize} + priority_weight_partition (int): + Partition factor used by priority/multifactor plugin in calculating + job priority. + + {slurm.conf#OPT_PriorityWeightPartition} + priority_weight_qos (int): + An integer value that sets the degree to which the Quality Of + Service component contributes to the job's priority + + {slurm.conf#OPT_PriorityWeightQOS} + priority_weight_tres (dict[str, int]): + TRES Types and weights that sets the degree that each TRES Type + contributes to the job's priority. + + {slurm.conf#OPT_PriorityWeightTRES} + private_data (list[str]): + Defines what type of information is hidden from regular users. + + {slurm.conf#OPT_PrivateData} + proctrack_type (str): + Identifies the plugin to be used for process tracking on a job step + basis. + + {slurm.conf#OPT_ProctrackType} + prolog (list[str]): + List of pathnames of programs for the slurmd to execute whenever + it is asked to run a job step from a new job allocation. + + {slurm.conf#OPT_Prolog} + prolog_epilog_timeout (int): + The interval in seconds Slurm waits for Prolog and Epilog before + terminating them. + + {slurm.conf#OPT_PrologEpilogTimeout} + prolog_slurmctld (list[str]): + List of pathnames of programs for the slurmctld daemon to execute + before granting a new job allocation. + + {slurm.conf#OPT_PrologSlurmctld} + propagate_prio_process (int): + Controls the scheduling priority (nice value) of user spawned + tasks. + + {slurm.conf#OPT_PropagatePrioProcess} + prolog_flags (list[str]): + Flags to control the Prolog behavior. + + {slurm.conf#OPT_PrologFlags} + propagate_resource_limits (list[str]): + List of resource limit names that are propagated to the Job + environment. + + {slurm.conf#OPT_PropagateResourceLimits} + propagate_resource_limits_except (list[str]): + List of resource limit names that are excluded from propagation to + the Job environment. + + {slurm.conf#OPT_PropagateResourceLimitsExcept} + reboot_program (str): + Program to be executed on each compute node to reboot it. + + {slurm.conf#OPT_RebootProgram} + reconfig_flags (list[str]): + List of flags to control various actions that may be taken when a + reconfigure command is issued (for example with `scontrol + reconfig`). + + {slurm.conf#OPT_ReconfigFlags} + requeue_exit (str): + Enables automatic requeue for batch jobs which exit with the + specified values. + + {slurm.conf#OPT_RequeueExit} + requeue_exit_hold (str): + Enables automatic requeue for batch jobs which exit with the + specified values, with these jobs being held until released + manually by the user. + + {slurm.conf#OPT_RequeueExitHold} + resume_fail_program (str): + The program that will be executed when nodes fail to resume to by + `resume_timeout`. + + {slurm.conf#OPT_ResumeFailProgram} + resume_program (str): + Program that will be executed when a node in power save mode is + assigned work to perform. + + {slurm.conf#OPT_ResumeProgram} + resume_rate (int): + Number of nodes per minute that will be restored from power save + mode to normal operation by `resume_program`. + + {slurm.conf#OPT_ResumeRate} + resume_timeout (int): + Maximum time permitted (in seconds) between when a node resume + request is issued and when the node is actually available for use. + + {slurm.conf#OPT_ResumeTimeout} + reservation_epilog (str): + Pathname of a program for the slurmctld to execute when a + reservation ends. + + {slurm.conf#OPT_ResvEpilog} + reservation_over_run (int): + Describes how long (in minutes) a job already running in a + reservation should be permitted to execute after the end time of + the reservation has been reached + + {slurm.conf#OPT_ResvOverRun} + reservation_prolog (str): + Pathname of a program for the slurmctld to execute when a + reservation begins. + + {slurm.conf#OPT_ResvProlog} + return_to_service (int): + Controls when a `DOWN` node will be returned to service + + {slurm.conf#OPT_ReturnToService} + scheduler_log_file (str): + Pathname of the scheduling event logging file. + + {slurm.conf#OPT_SlurmSchedLogFile} + scheduler_logging_enabled (bool): + The initial level of scheduling event logging. + + {slurm.conf#OPT_SlurmSchedLogLevel} + scheduler_parameters (dict[str, Union[str, int, bool]]): + List of options for the `scheduler_type` plugin. + + If a value in this dict is `True`, it means this parameter does not + have any additional options specified, and is just an "enabled" + option. + + {slurm.conf#OPT_SchedulerParameters} + scheduler_time_slice (int): + Number of seconds in each time slice when gang scheduling is + enabled. + + {slurm.conf#OPT_SchedulerTimeSlice} + scheduler_type (str): + Identifies the type of scheduler to be used. + + {slurm.conf#OPT_SchedulerType} + scron_parameters (list[str]): + Parameters for scron. + + {slurm.conf#OPT_ScronParameters} + select_type (str): + Identifies the type of resource selection algorithm to be used. + + {slurm.conf#OPT_SelectType} + select_type_parameters (list[str]): + Parameters passed to the `select_type` plugin. + + {slurm.conf#OPT_SelectTypeParameters} + priority_site_factor_plugin (str): + Specifies an optional plugin to be used alongside + "priority/multifactor", which is meant to initially set and + continuously update the SiteFactor priority factor. + + {slurm.conf#OPT_PrioritySiteFactorPlugin} + priority_site_factor_parameters (str): + Arbitrary string used by the PrioritySiteFactorPlugin plugin. + + {slurm.conf#OPT_PrioritySiteFactorParameters} + slurm_conf_path (str): + Path of the current slurm.conf file used. + slurm_user_id (int): + UID of the `slurm_user_Name` + slurm_user_name (str): + Name of the Slurm User + slurmd_user_id (int): + UID of the `slurmd_user_name` + slurmd_user_name (str): + Name of the User slurmd runs as. + slurmctld_log_level (str): + The level of detail to provide `slurmctld` daemon's logs. + + {slurm.conf#OPT_SlurmctldDebug} + slurmctld_log_file (str): + Pathname of a file into which the `slurmctld` daemon's logs are + written. + + {slurm.conf#OPT_SlurmctldLogFile} + slurmctld_pid_file (str): + Pathname of a file into which the `slurmctld` daemon may write its + process id. + + {slurm.conf#OPT_SlurmctldPidFile} + slurmctld_port (str): + Port number where `slurmctld` listens to for work. + Note that this can also be a port range. + + {slurm.conf#OPT_SlurmctldPort} + slurmctld_primary_off_program (str): + This program is executed when a `slurmctld` daemon running as the + primary server becomes a backup server. + + {slurm.conf#OPT_SlurmctldPrimaryOffProg} + slurmctld_primary_on_program (str): + This program is executed when a `slurmctld` daemon running as a + backup server becomes the primary server. + + {slurm.conf#OPT_SlurmctldPrimaryOnProg} + slurmctld_syslog_level (str): + Level of detail that the `slurmctld` logs to the syslog. + + {slurm.conf#OPT_SlurmctldSyslogDebug} + slurmctld_timeout (int): + The interval, in seconds, that the backup controller waits for the + primary controller to respond before assuming control. + + {slurm.conf#OPT_SlurmctldTimeout} + slurmctld_parameters (dict[str, Union[str, int, bool]]): + Options set for the `slurmctld`. + + If a value in this dict is `True`, it means this parameter does not + have any additional options specified, and is just an "enabled" + option. + + {slurm.conf#OPT_SlurmctldParameters} + slurmd_log_level (str): + Level of detail `slurmd` is logging. + + {slurm.conf#OPT_SlurmdDebug} + slurmd_log_file (str): + Pathname of the file where `slurmd` writes logs to. + + {slurm.conf#OPT_SlurmdLogFile} + slurmd_parameters (dict[str, Union[str, int, bool]]): + Parameters for the `slurmd`. + + If a value in this dict is `True`, it means this parameter does not + have any additional options specified, and is just an "enabled" + option. + + {slurm.conf#OPT_SlurmdParameters} + slurmd_pid_file (str): + Pathname of a file into which the `slurmd` daemon may write its + process id. + + {slurm.conf#OPT_SlurmdPidFile} + slurmd_port (int): + Port number where `slurmd` listens to for work. + + {slurm.conf#OPT_SlurmdPort} + slurmd_spool_directory (str): + Pathname of a directory into which the `slurmd` daemon's state + information and batch job script information are written. + + {slurm.conf#OPT_SlurmdSpoolDir} + slurmd_syslog_level (str): + Level of detail that the `slurmd` logs to the syslog. + + {slurm.conf#OPT_SlurmdSyslogDebug} + slurmd_timeout (int): + The interval, in seconds, that `slurmctld` waits for `slurmd` to + respond before configuring that node's state to `DOWN`. + + {slurm.conf#OPT_SlurmdTimeout} + srun_epilog (str): + Pathname of an executable to be run by `srun` following the + completion of a job step. + + {slurm.conf#OPT_SrunEpilog} + srun_port_range (str): + Ports `srun` creates to communicate with the `slurmctld`, the + `slurmstepd` and to handle the application I/O. + + {slurm.conf#OPT_SrunPortRange} + srun_prolog (str): + Pathname of an executable to be run by `srun` prior to the launch + of a job step. + + {slurm.conf#OPT_SrunProlog} + state_save_location (str): + Pathname of a directory where `slurmctld` saves its state. + + {slurm.conf#OPT_StateSaveLocation} + suspend_exclude_nodes (str): + Specifies the nodes which are to not be placed in power save mode, + even if the node remains idle for an extended period of time. + + {slurm.conf#OPT_SuspendExcNodes} + suspend_exclude_partitions (str): + Specifies the partitions whose nodes are to not be placed in power + save mode, even if the node remains idle for an extended period of + time. + + {slurm.conf#OPT_SuspendExcParts} + suspend_exclude_states (list[str]): + Specifies node states that are not to be powered down + automatically. + + {slurm.conf#OPT_SuspendExcStates} + suspend_program (str): + Program that will be executed when a node remains idle for an + extended period of time. + + {slurm.conf#OPT_SuspendProgram} + suspend_rate (int): + Number of nodes per minute that are placed into power save mode. + + {slurm.conf#OPT_SuspendRate} + suspend_time (int): + Nodes which remain idle or down for this number of seconds will be + placed into power save mode. + + {slurm.conf#OPT_SuspendTime} + suspend_timeout (int): + Maximum time permitted (in seconds) between when a node suspend + request is issued and when the node is shutdown. + + {slurm.conf#OPT_SuspendTimeout} + switch_type (str): + Identifies the type of switch or interconnect used for application + communications. + + {slurm.conf#OPT_SwitchType} + switch_parameters (dict[str, Union[str, int, bool]]): + Optional parameters for the switch plugin. + + If a value in this dict is `True`, it means this parameter does not + have any additional options specified, and is just an "enabled" + option. + + {slurm.conf#OPT_SwitchParameters} + task_epilog (str): + Pathname of a program to be executed as the slurm job's owner after + termination of each task. + + {slurm.conf#OPT_TaskEpilog} + task_plugin (str): + Identifies the type of task launch plugin, typically used to + provide resource management within a node. + + {slurm.conf#OPT_TaskPlugin} + task_plugin_parameters (list[str]): + Optional Parameters for `task_plugin`. + + {slurm.conf#OPT_TaskPluginParam} + task_prolog (str): + Pathname of a program to be executed as the slurm job's owner prior + to initiation of each task. + + {slurm.conf#OPT_TaskProlog} + tls_parameters (list[str]): + Parameters for `tls_type`. + tls_type (str): + TLS Plugin used. + tcp_timeout (int): + Time permitted for TCP connection to be established. + + {slurm.conf#OPT_TCPTimeout} + temporary_filesystem (str): + Pathname of the file system available to user jobs for temporary + storage. + + {slurm.conf#OPT_TmpFS} + topology_parameters (dict[str, Union[str, int, bool]]): + List of network topology options + + If a value in this dict is `True`, it means this parameter does not + have any additional options specified, and is just an "enabled" + option. + + {slurm.conf#OPT_TopologyParam} + topology_plugin (str): + Identifies the plugin to be used for determining the network + topology and optimizing job allocations to minimize network + contention. + + {slurm.conf#OPT_TopologyPlugin} + tree_width (int): + Specifies the width of the virtual network tree `slurmd` uses for + communication. + + {slurm.conf#OPT_TreeWidth} + unkillable_step_program (str): + Program that will be executed when the processes in a job step are + determined unkillable. + + {slurm.conf#OPT_UnkillableStepProgram} + unkillable_step_timeout (int): + The length of time, in seconds, that Slurm will wait before + deciding that processes in a job step are unkillable. + + {slurm.conf#OPT_UnkillableStepTimeout} + track_wckey (bool): + Whether WCKeys are tracked or not. + + {slurm.conf#OPT_TrackWCKey} + use_pam (bool): + Whether PAM (Pluggable Authentication Modules for Linux) will be + enabled or not. + + {slurm.conf#OPT_UsePAM} + version (str): + Version as returned by the `slurmctld`. + virtual_memory_size_factor (int): + Specifies the job's or job step's virtual memory limit as a + percentage of its real memory limit. + + {slurm.conf#OPT_VSizeFactor} + wait_time (int): + Specifies how many seconds the srun command should by default wait + after the first task terminates before terminating all remaining + tasks. + + {slurm.conf#OPT_WaitTime} + x11_parameters (list[str]): + Parameters for Slurm's built-in X11 forwarding implementation. + + {slurm.conf#OPT_X11Parameters} + """ + cdef slurm_conf_t *ptr + + cdef public: + CgroupConfig cgroup_config + AccountingGatherConfig accounting_gather_config + MPIConfig mpi_config + + +# Documentation for the attributes in the MPIConfig class have +# been largely taken from the official mpi.conf overview at: +# https://slurm.schedmd.com/mpi.conf.html +# +# Therefore, the following Copyright notices that mpi.conf has (see +# https://slurm.schedmd.com/mpi.conf.html#SECTION_COPYING), are also +# listed here: +# +# Copyright (C) 2022 SchedMD LLC. +cdef class MPIConfig: + """Slurm MPI Config (`mpi.conf`) + + Attributes: + pmix_cli_tmp_dir_base (str): + Directory to have PMIx use for temporary files. + + {mpi.conf#OPT_PMIxCliTmpDirBase} + pmix_coll_fence (str): + Defines the type of fence to use for collecting inter-node data. + + {mpi.conf#OPT_PMIxCollFence} + pmix_debug (bool): + Whether debug logging for the PMIx Plugin is enabled or not. + + {mpi.conf#OPT_PMIxDebug} + pmix_direct_conn (bool): + Whether direct launching of tasks is enabled or not. + + {mpi.conf#OPT_PMIxDirectConn} + pmix_direct_conn_early (bool): + Whether early connection to a parent node are allowed or not. + + {mpi.conf#OPT_PMIxDirectConnEarly} + pmix_direct_conn_ucx (bool): + Whether PMIx is allowed to use UCX for communication. + + {mpi.conf#OPT_PMIxDirectConnUCX} + pmix_direct_same_arch (bool): + Whether additional communication optimizations are enabled when + `pmix_direct_conn` is also set to `True`, also assuming all nodes + of the job have the same architecture. + + {mpi.conf#OPT_PMIxDirectSameArch} + pmix_environment (dict[str, Union[str, int]]): + Environment variables to bet set in the Job environment, used by + PMIx. + + {mpi.conf#OPT_PMIxEnv} + pmix_fence_barrier (bool): + Whether to fence inter-node communication for data collection. + + {mpi.conf#OPT_PMIxFenceBarrier} + pmix_net_devices_ucx (str): + Type of network device to use for communication. + + {mpi.conf#OPT_PMIxNetDevicesUCX} + pmix_timeout (int): + The maximum time (in seconds) allowed for communication between + hosts to take place. + + {mpi.conf#OPT_PMIxTimeout} + pmix_tls_ucx (list[str]): + List of values for the UCX_TLS variable which restrict the + transports to use. + + {mpi.conf#OPT_PMIxTlsUCX} + """ + cdef public: + pmix_cli_tmp_dir_base + pmix_coll_fence + pmix_debug + pmix_direct_conn + pmix_direct_conn_early + pmix_direct_conn_ucx + pmix_direct_same_arch + pmix_environment + pmix_fence_barrier + pmix_net_devices_ucx + pmix_timeout + pmix_tls_ucx + + @staticmethod + cdef MPIConfig from_ptr(void *ptr) + + +# Documentation for the attributes in the CgroupConfig class have +# been largely taken from the official cgroup.conf overview at: +# https://slurm.schedmd.com/cgroup.conf.html +# +# Therefore, the following Copyright notices that cgroup.conf has (see +# https://slurm.schedmd.com/cgroup.conf.html#SECTION_COPYING), are also +# listed here: +# +# Copyright (C) 2010-2012 Lawrence Livermore National Security. (cf, +# pyslurm/slurm/SLURM_DISCLAIMER). +# Copyright (C) 2010-2022 SchedMD LLC. +cdef class CgroupConfig: + """Slurm Cgroup Config (`cgroup.conf`) + + Attributes: + mountpoint (str): + Specifies the PATH under which cgroup controllers should be + mounted. + + {cgroup.conf#OPT_CgroupMountpoint} + plugin (str): + Specifies the plugin to be used when interacting with the cgroup + subsystem. + + {cgroup.conf#OPT_CgroupPlugin} + systemd_timeout (int): + Maximum time (in milliseconds) that Slurm will wait for the slurmd + scope to be ready before failing. + + {cgroup.conf#OPT_SystemdTimeout} + ignore_systemd (bool): + If `True`, it will avoid any call to dbus and contact with systemd, + and cgroup hierarchy preparation is done manually. Only for + `cgroup/v2` + + {cgroup.conf#OPT_IgnoreSystemd} + ignore_systemd_on_failure (bool): + Similar to `ignore_systemd`, but only in the case that a dbus call + does not succeed. Only for `cgroup/v2`. + + {cgroup.conf#OPT_IgnoreSystemdOnFailure} + enable_controllers (bool): + When enabled, `slurmd` gets the available controllers from root`s + cgroup.controllers file located in `mountpoint`. + + {cgroup.conf#OPT_EnableControllers} + allowed_ram_space (int): + Constrains the job/step cgroup RAM to this percentage of the + allocated memory. + + {cgroup.conf#OPT_AllowedRAMSpace} + allowed_swap_space (float): + Constrain the job cgroup swap space to this percentage of the + allocated memory. + + {cgroup.conf#OPT_AllowedSwapSpace} + constrain_cores (bool): + When `True`, then constrain allowed cores to the subset of + allocated resources. + + {cgroup.conf#OPT_ConstrainCores} + constrain_devices (bool): + When `True`, then constrain the job's allowed devices based on GRES + allocated resources. + + {cgroup.conf#OPT_ConstrainDevices} + constrain_ram_space (bool): + When `True`, then constrain the job's RAM usage by setting the + memory soft limit to the allocated memory and the hard limit to the + allocated memory * `allowed_ram_space`. + + {cgroup.conf#OPT_ConstrainRAMSpace} + constrain_swap_space (bool): + When `True`, then constrain the job's swap space usage. + + {cgroup.conf#OPT_ConstrainSwapSpace} + max_ram_percent (float): + Upper bound in percent of total RAM (configured RealMemory of the + node) on the RAM constraint for a job. + + {cgroup.conf#OPT_MaxRAMPercent} + max_swap_percent (float): + Upper bound (in percent of total RAM, configured RealMemory of the + node) on the amount of RAM+Swap that may be used for a job. + + {cgroup.conf#OPT_MaxSwapPercent} + memory_swappiness (float): + Configures the kernel's priority for swapping out anonymous pages + verses file cache pages for the job cgroup. Only for `cgroup/v1`. + A value of `-1.0` means that the kernel's default swappiness value + will be used. + + {cgroup.conf#OPT_MemorySwappiness} + min_ram_space (int): + Lower bound (in Mebibytes) on the memory limits defined by + `allowed_ram_space` and `allowed_swap_space`. + + {cgroup.conf#OPT_MinRAMSpace} + signal_children_processes (bool): + When `True`, then send signals (for cancelling, suspending, + resuming, etc.) to all children processes in a job/step. + + {cgroup.conf#OPT_SignalChildrenProcesses} + """ + cdef public: + mountpoint + plugin + systemd_timeout + ignore_systemd + ignore_systemd_on_failure + enable_controllers + + allowed_ram_space + allowed_swap_space + constrain_cores + constrain_devices + constrain_ram_space + constrain_swap_space + max_ram_percent + max_swap_percent + memory_swappiness + min_ram_space + + signal_children_processes + + @staticmethod + cdef CgroupConfig from_ptr(void *ptr) + + +# Documentation for the attributes in the AccountingGatherConfig class have +# been largely taken from the official acct_gather.conf overview at: +# https://slurm.schedmd.com/acct_gather.conf.html +# +# Therefore, the following Copyright notices that acct_gather.conf has (see +# https://slurm.schedmd.com/acct_gather.conf.html#SECTION_COPYING), are also +# listed here: +# +# Copyright (C) 2012-2013 Bull. +# Copyright (C) 2012-2022 SchedMD LLC. +cdef class AccountingGatherConfig: + """Slurm Accounting Gather Config (`acct_gather.conf`) + + Attributes: + energy_ipmi_frequency (int): + Number of seconds between BMC access samples or XCC samples, + depending on the plugin used. + + {acct_gather.conf#OPT_EnergyIPMIFrequency} + energy_ipmi_calc_adjustment (bool): + When `True`, the consumption between the last BMC access sample and + a step consumption update is approximated to get more accurate task + consumption. + + {acct_gather.conf#OPT_EnergyIPMICalcAdjustment} + energy_ipmi_power_sensors (str): + IDs of the sensors to used. + + {acct_gather.conf#OPT_EnergyIPMIPowerSensors} + energy_ipmi_user_name (str): + BMC Username + + {acct_gather.conf#OPT_EnergyIPMIUsername} + energy_ipmi_password (str): + BMC Password + + {acct_gather.conf#OPT_EnergyIPMIPassword} + energy_ipmi_timeout (int): + Timeout, in seconds, for initializing the IPMI XCC context for a + new gathering thread. Default is 10 seconds. + + {acct_gather.conf#OPT_EnergyIPMITimeout} + profile_hdf5_dir (str): + Path to the shared folder into which the `acct_gather_profile` + plugin will write detailed data. + + {acct_gather.conf#OPT_ProfileHDF5Dir} + profile_hdf5_default (list[str]): + List of data types to be collected for each job submission. + + {acct_gather.conf#OPT_ProfileHDF5Default} + profile_influxdb_database (str): + InfluxDB v1.x database name where profiling information is to be + written. InfluxDB v2.x bucket name where profiling information is + to be written. + + {acct_gather.conf#OPT_ProfileInfluxDBDatabase} + profile_influxdb_default (list[str]): + List of data types to be collected for each job submission. + + {acct_gather.conf#OPT_ProfileInfluxDBDefault} + profile_influxdb_host (str): + The hostname of the machine where the InfluxDB instance is executed + and the port used by the HTTP API. + + {acct_gather.conf#OPT_ProfileInfluxDBHost} + profile_influxdb_password (str): + Password for `profile_influxdb_user` + + {acct_gather.conf#OPT_ProfileInfluxDBPass} + profile_influxdb_rtpolicy (str): + The InfluxDB v1.x retention policy name for the database configured + in ProfileInfluxDBDatabase option. The InfluxDB v2.x retention + policy bucket name for the database configured in + ProfileInfluxDBDatabase option. + + {acct_gather.conf#OPT_ProfileInfluxDBRTPolicy} + profile_influxdb_user (str): + InfluxDB username that should be used to gain access to the + database configured in `profile_influxdb_database`. + + {acct_gather.conf#OPT_ProfileInfluxDBRTUser} + profile_influxdb_timeout (int): + The maximum time in seconds that an HTTP query to the InfluxDB + server can take. + + {acct_gather.conf#OPT_ProfileInfluxDBTimeout} + infiniband_ofed_port (int): + Represents the port number of the local Infiniband card that we are + willing to monitor. + + {acct_gather.conf#OPT_InfinibandOFEDPort} + sysfs_interfaces (list[str]): + List of interface names to collect statistics from. + + {acct_gather.conf#OPT_SysfsInterfaces} + """ + cdef public: + energy_ipmi_frequency + energy_ipmi_calc_adjustment + energy_ipmi_power_sensors + energy_ipmi_user_name + energy_ipmi_password + energy_ipmi_timeout + + profile_hdf5_dir + profile_hdf5_default + + profile_influxdb_database + profile_influxdb_default + profile_influxdb_host + profile_influxdb_password + profile_influxdb_rtpolicy + profile_influxdb_user + profile_influxdb_timeout + + infiniband_ofed_port + + sysfs_interfaces + + @staticmethod + cdef AccountingGatherConfig from_ptr(void *ptr) diff --git a/pyslurm/core/slurmctld/config.pyx b/pyslurm/core/slurmctld/config.pyx new file mode 100644 index 00000000..f72cac75 --- /dev/null +++ b/pyslurm/core/slurmctld/config.pyx @@ -0,0 +1,1253 @@ +######################################################################### +# slurmctld/config.pyx - pyslurm slurmctld config api +######################################################################### +# Copyright (C) 2025 Toni Harzendorf +# +# This file is part of PySlurm +# +# PySlurm is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# PySlurm is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with PySlurm; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# cython: c_string_type=unicode, c_string_encoding=default +# cython: language_level=3 + +from pyslurm.core.error import verify_rpc, RPCError +from pyslurm.utils.uint import ( + u16_parse, + u32_parse, + u64_parse, +) +from pyslurm.constants import UNLIMITED +from pyslurm.utils.ctime import _raw_time +from pyslurm.utils.helpers import ( + cpu_freq_int_to_str, + instance_to_dict, +) +from pyslurm.utils import cstr +from typing import Union + + +cdef class MPIConfig: + + def __init__(self): + raise RuntimeError("Cannot instantiate class directly. " + "Use slurmctld.Config.load() and access the " + "mpi_config attribute there") + + def to_dict(self): + """MPI config formatted as a dictionary. + + Returns: + (dict): Config as a dict + + Examples: + >>> from pyslurm import slurmctld + >>> config = slurmctld.Config.load() + >>> mpi_config = config.mpi_config.to_dict() + """ + return instance_to_dict(self) + + @staticmethod + cdef MPIConfig from_ptr(void *ptr): + cdef: + dict conf = _parse_config_key_pairs(ptr) + MPIConfig out = MPIConfig.__new__(MPIConfig) + + out.pmix_cli_tmp_dir_base = conf.get("PMIxCliTmpDirBase") + out.pmix_coll_fence = conf.get("PMIxCollFence") + out.pmix_debug = bool(int(conf.get("PMIxDebug", 0))) + out.pmix_direct_conn = _true_false_to_bool(conf.get("PMIxDirectConn", "true")) + out.pmix_direct_conn_early = _true_false_to_bool(conf.get("PMIxDirectConnEarly", "false")) + out.pmix_direct_conn_ucx = _true_false_to_bool(conf.get("PMIxDirectConnUCX", "false")) + out.pmix_direct_same_arch = _true_false_to_bool(conf.get("PMIxDirectSameArch", "false")) + out.pmix_environment = cstr.to_dict( + conf.get("PMIxEnv", ""), delim1=";", delim2="=") + out.pmix_fence_barrier = _true_false_to_bool(conf.get("PMIxFenceBarrier", "false")) + out.pmix_net_devices_ucx = conf.get("PMIxNetDevicesUCX") + out.pmix_timeout = int(conf.get("PMIxTimeout", 300)) + out.pmix_tls_ucx = cstr.to_list(conf.get("PMIxTlsUCX", "")) + + return out + + +cdef class CgroupConfig: + + def __init__(self): + raise RuntimeError("Cannot instantiate class directly. " + "Use slurmctld.Config.load() and access the " + "cgroup_config attribute there") + + def to_dict(self): + """Cgroup config formatted as a dictionary. + + Returns: + (dict): Config as a dict + + Examples: + >>> from pyslurm import slurmctld + >>> config = slurmctld.Config.load() + >>> cgroup_config = config.cgroup_config.to_dict() + """ + return instance_to_dict(self) + + @staticmethod + cdef CgroupConfig from_ptr(void *ptr): + cdef: + dict conf = _parse_config_key_pairs(ptr) + CgroupConfig out = CgroupConfig.__new__(CgroupConfig) + + out.mountpoint = conf.get("CgroupMountpoint", "/sys/fs/cgroup") + out.plugin = conf.get("CgroupPlugin", "autodetect") + out.systemd_timeout = int(conf.get("SystemdTimeout", 1000)) + out.ignore_systemd = _yesno_to_bool(conf.get("IgnoreSystemd")) + out.ignore_systemd_on_failure = _yesno_to_bool(conf.get("IgnoreSystemdOnFailure")) + out.enable_controllers = _yesno_to_bool(conf.get("EnableControllers")) + + out.allowed_ram_space = float(conf.get("AllowedRAMSpace", 100.0)) + out.allowed_swap_space = float(conf.get("AllowedSwapSpace", 0.0)) + out.constrain_cores = _yesno_to_bool(conf.get("ConstrainCores", "no")) + out.constrain_devices = _yesno_to_bool(conf.get("ConstrainDevices", "no")) + out.constrain_ram_space = _yesno_to_bool(conf.get("ConstrainRAMSpace", "no")) + out.constrain_swap_space = _yesno_to_bool(conf.get("ConstrainSwapSpace", "no")) + out.max_ram_percent = float(conf.get("MaxRAMPercent", 100.0)) + out.max_swap_percent = float(conf.get("MaxSwapPercent", 100.0)) + out.memory_swappiness = float(conf.get("MemorySwappiness", -1.0)) + out.min_ram_space = int(conf.get("MinRAMSpace", 30*1024)) + + out.signal_children_processes = _yesno_to_bool(conf.get("SignalChildrenProcesses", "no")) + + return out + +cdef class AccountingGatherConfig: + + def __init__(self): + raise RuntimeError("Cannot instantiate class directly. " + "Use slurmctld.Config.load() and access the " + "accounting_gather_config attribute there") + + def to_dict(self): + """AccountingGather config formatted as a dictionary. + + Returns: + (dict): Config as a dict + + Examples: + >>> from pyslurm import slurmctld + >>> config = slurmctld.Config.load() + >>> acctg_config_dict = config.accounting_gather_config.to_dict() + """ + return instance_to_dict(self) + + @staticmethod + cdef AccountingGatherConfig from_ptr(void *ptr): + cdef: + dict conf = _parse_config_key_pairs(ptr) + AccountingGatherConfig out = AccountingGatherConfig.__new__(AccountingGatherConfig) + + out.energy_ipmi_frequency = int(conf.get("EnergyIPMIFrequency", 30)) + out.energy_ipmi_calc_adjustment = _yesno_to_bool( + conf.get("EnergyIPMICalcAdjustment")) + out.energy_ipmi_power_sensors = cstr.to_dict( + conf.get("EnergyIPMIPowerSensors", ""), delim1=";", delim2="=") + out.energy_ipmi_user_name = conf.get("EnergyIPMIUsername") + out.energy_ipmi_password = conf.get("EnergyIPMIPassword") + out.energy_ipmi_timeout = int(conf.get("EnergyIPMITimeout", 10)) + + out.profile_hdf5_dir = conf.get("ProfileHDF5Dir") + out.profile_hdf5_default = conf.get("ProfileHDF5Default", "").split(",") + + out.profile_influxdb_database = conf.get("ProfileInfluxDBDatabase") + out.profile_influxdb_default = conf.get("ProfileInfluxDBDefault", "").split(",") + out.profile_influxdb_host = conf.get("ProfileInfluxDBHost") + out.profile_influxdb_password = conf.get("ProfileInfluxDBPass") + out.profile_influxdb_rtpolicy = conf.get("ProfileInfluxDBRTPolicy") + out.profile_influxdb_user = conf.get("ProfileInfluxDBUser") + out.profile_influxdb_timeout = int(conf.get("ProfileInfluxDBTimeout", 10)) + + out.infiniband_ofed_port = int(conf.get("InfinibandOFEDPort", 1)) + out.sysfs_interfaces = conf.get("SysfsInterfaces", []) + + return out + + +cdef class Config: + + def __cinit__(self): + self.ptr = NULL + + def __init__(self): + raise RuntimeError("Cannot instantiate class directly. " + "Use slurmctld.Config.load() to get an instance.") + + def __dealloc__(self): + slurm_free_ctl_conf(self.ptr) + self.ptr = NULL + + @staticmethod + def load(): + """Load the current Slurm configuration (slurm.conf) + + This also loads the following other configurations: + * `cgroup.conf` (`cgroup_config`) + * `acct_gather.conf` (`accounting_gather_config`) + * `mpi.conf` (`mpi_config`) + """ + cdef Config conf = Config.__new__(Config) + verify_rpc(slurm_load_ctl_conf(0, &conf.ptr)) + + conf.cgroup_config = CgroupConfig.from_ptr(conf.ptr.cgroup_conf) + conf.accounting_gather_config = AccountingGatherConfig.from_ptr( + conf.ptr.acct_gather_conf) + conf.mpi_config = MPIConfig.from_ptr(conf.ptr.mpi_conf) + # TODO: node_features_conf + + return conf + + def to_dict(self): + """Slurmctld config formatted as a dictionary. + + Returns: + (dict): slurmctld config as a dict + + Examples: + >>> import pyslurm + >>> config = pyslurm.slurmctld.Config.load() + >>> config_dict = config.as_dict() + """ + out = instance_to_dict(self) + out["cgroup_config"] = self.cgroup_config.to_dict() + out["accounting_gather_config"] = self.accounting_gather_config.to_dict() + out["mpi_config"] = self.mpi_config.to_dict() + return out + + @property + def accounting_storage_enforce(self): + cdef char tmp[128] + slurm_accounting_enforce_string(self.ptr.accounting_storage_enforce, + tmp, sizeof(tmp)) + out = cstr.to_unicode(tmp) + if not out or out == "none": + return [] + + return out.upper().split(",") + + @property + def accounting_storage_backup_host(self): + return cstr.to_unicode(self.ptr.accounting_storage_backup_host) + + @property + def accounting_storage_external_hosts(self): + return cstr.to_list(self.ptr.accounting_storage_ext_host) + + @property + def accounting_storage_host(self): + return cstr.to_unicode(self.ptr.accounting_storage_host) + + @property + def accounting_storage_parameters(self): + return cstr.to_dict(self.ptr.accounting_storage_params, delim1=",", + delim2="=", def_value=True) + + @property + def accounting_storage_port(self): + return u16_parse(self.ptr.accounting_storage_port) + + @property + def accounting_storage_tres(self): + return cstr.to_list(self.ptr.accounting_storage_tres) + + @property + def accounting_storage_type(self): + return cstr.to_unicode(self.ptr.accounting_storage_type) + + @property + def accounting_storage_user(self): + return cstr.to_unicode(self.ptr.accounting_storage_user) + + @property + def accounting_store_flags(self): + out = [] + flags = self.ptr.conf_flags + + if flags & slurm.CONF_FLAG_SJC: + out.append("JOB_COMMENT") + if flags & slurm.CONF_FLAG_SJE: + out.append("JOB_ENV") + if flags & slurm.CONF_FLAG_SJX: + out.append("JOB_EXTRA") + if flags & slurm.CONF_FLAG_SJS: + out.append("JOB_SCRIPT") + if flags & slurm.CONF_FLAG_NO_STDIO: + out.append("NO_STDIO") + + return out + + @property + def accounting_gather_node_frequency(self): + return u16_parse(self.ptr.acct_gather_node_freq) + + @property + def accounting_gather_energy_type(self): + return cstr.to_unicode(self.ptr.acct_gather_energy_type) + + @property + def accounting_gather_interconnect_type(self): + return cstr.to_unicode(self.ptr.acct_gather_interconnect_type) + + @property + def accounting_gather_filesystem_type(self): + return cstr.to_unicode(self.ptr.acct_gather_filesystem_type) + + @property + def accounting_gather_profile_type(self): + return cstr.to_unicode(self.ptr.acct_gather_profile_type) + + @property + def allow_spec_resource_usage(self): + if self.ptr.conf_flags & slurm.CONF_FLAG_ASRU: + return True + + return False + + @property + def auth_alt_types(self): + return cstr.to_list(self.ptr.authalttypes) + + @property + def auth_info(self): + return cstr.to_list(self.ptr.authinfo) + + @property + def auth_alt_parameters(self): + return cstr.to_dict(self.ptr.authalt_params, delim1=",", + delim2="=", def_value=True) + + @property + def auth_type(self): + return cstr.to_unicode(self.ptr.authtype) + + @property + def batch_start_timeout(self): + return u16_parse(self.ptr.batch_start_timeout) + + @property + def bcast_exclude_paths(self): + return cstr.to_list(self.ptr.bcast_exclude) + + @property + def bcast_parameters(self): + return cstr.to_dict(self.ptr.authalt_params, delim1=",", + delim2="=", def_value=True) + + @property + def burst_buffer_type(self): + return cstr.to_unicode(self.ptr.bb_type) + + @property + def slurmctld_boot_time(self): + return _raw_time(self.ptr.boot_time) + + @property + def certmgr_parameters(self): + return cstr.to_list(self.ptr.certmgr_params) + + @property + def certmgr_type(self): + return cstr.to_unicode(self.ptr.certmgr_type) + + @property + def cli_filter_plugins(self): + return cstr.to_list(self.ptr.cli_filter_plugins) + + @property + def cluster_name(self): + return cstr.to_unicode(self.ptr.cluster_name) + + @property + def communication_parameters(self): + return cstr.to_dict(self.ptr.comm_params, delim1=",", + delim2="=", def_value=True) + + @property + def complete_wait_time(self): + return u16_parse(self.ptr.complete_wait) + + @property + def default_cpu_frequency_governor(self): + return cpu_freq_int_to_str(self.ptr.cpu_freq_def) + + @property + def cpu_frequency_governors(self): + return cpu_freq_int_to_str(self.ptr.cpu_freq_govs) + + @property + def credential_type(self): + return cstr.to_unicode(self.ptr.cred_type) + + @property + def data_parser_parameters(self): + return cstr.to_unicode(self.ptr.data_parser_parameters) + + @property + def debug_flags(self): + cdef char *data = slurm.debug_flags2str(self.ptr.debug_flags) + return cstr.to_list_free(&data) + + @property + def default_memory_per_cpu(self): + return _get_memory(self.ptr.def_mem_per_cpu, per_cpu=True) + + @property + def default_memory_per_node(self): + return _get_memory(self.ptr.def_mem_per_cpu, per_cpu=False) + + # TODO: DefCpuPerGPU + # TODO: DefMemPerGPU + + @property + def dependency_parameters(self): + return cstr.to_dict(self.ptr.dependency_params, delim1=",", + delim2="=", def_value=True) + + @property + def disable_root_jobs(self): + if self.ptr.conf_flags & slurm.CONF_FLAG_DRJ: + return True + return False + + @property + def eio_timeout(self): + return u16_parse(self.ptr.eio_timeout) + + @property + def enforce_partition_limits(self): + cdef char* data = slurm.parse_part_enforce_type_2str( + self.ptr.enforce_part_limits) + return cstr.to_unicode(data) + + @property + def epilog(self): + return cstr.to_list_with_count(self.ptr.epilog, + self.ptr.epilog_cnt) + + @property + def epilog_msg_time(self): + return u32_parse(self.ptr.epilog_msg_time) + + @property + def epilog_slurmctld(self): + return cstr.to_list_with_count(self.ptr.epilog_slurmctld, + self.ptr.epilog_slurmctld_cnt) + + @property + def fair_share_dampening_factor(self): + return u16_parse(self.ptr.fs_dampening_factor) + + @property + def federation_parameters(self): + return cstr.to_list(self.ptr.fed_params) + + @property + def first_job_id(self): + return u32_parse(self.ptr.first_job_id) + + @property + def get_environment_timeout(self): + return u16_parse(self.ptr.get_env_timeout) + + @property + def gres_types(self): + return cstr.to_list(self.ptr.gres_plugins) + + @property + def group_update_force(self): + return u16_parse_bool(self.ptr.group_force) + + @property + def group_update_time(self): + return u16_parse(self.ptr.group_time) + + @property + def default_gpu_frequency(self): + return cstr.to_unicode(self.ptr.gpu_freq_def) + + @property + def hash_plugin(self): + return cstr.to_unicode(self.ptr.hash_plugin) + + @property + def hash_value(self): + val = u32_parse(self.ptr.hash_val) + if not val: + return None + return hex(val) + + @property + def health_check_interval(self): + return u16_parse(self.ptr.health_check_interval) + + @property + def health_check_node_state(self): + cdef char *data = slurm.health_check_node_state_str( + self.ptr.health_check_node_state) + return cstr.to_list_free(&data) + + @property + def health_check_program(self): + return cstr.to_unicode(self.ptr.health_check_program) + + @property + def inactive_limit(self): + return u16_parse(self.ptr.inactive_limit) + + @property + def interactive_step_options(self): + return cstr.to_unicode(self.ptr.interactive_step_opts) + + @property + def job_accounting_gather_type(self): + return cstr.to_unicode(self.ptr.job_acct_gather_type) + + @property + def job_accounting_gather_frequency(self): + return cstr.to_dict(self.ptr.job_acct_gather_freq) + + @property + def job_accounting_gather_parameters(self): + return cstr.to_list(self.ptr.job_acct_gather_params) + + @property + def job_completion_host(self): + return cstr.to_unicode(self.ptr.job_comp_host) + + @property + def job_completion_location(self): + return cstr.to_unicode(self.ptr.job_comp_loc) + + @property + def job_completion_parameters(self): + return cstr.to_dict(self.ptr.job_comp_params, delim1=",", + delim2="=", def_value=True) + + @property + def job_completion_port(self): + return u32_parse(self.ptr.job_comp_port) + + @property + def job_completion_type(self): + return cstr.to_unicode(self.ptr.job_comp_type) + + @property + def job_completion_user(self): + return cstr.to_unicode(self.ptr.job_comp_user) + + @property + def job_container_type(self): + return cstr.to_unicode(self.ptr.job_container_plugin) + + @property + def job_defaults(self): + cdef char *data = slurm.job_defaults_str(self.ptr.job_defaults_list) + out = cstr.to_dict(data) + xfree(data) + return out + + @property + def job_file_append(self): + return u16_parse_bool(self.ptr.job_file_append) + + @property + def job_requeue(self): + return u16_parse_bool(self.ptr.job_requeue) + + @property + def job_submit_plugins(self): + return cstr.to_list(self.ptr.job_submit_plugins) + + @property + def kill_on_bad_exit(self): + return u16_parse_bool(self.ptr.kill_on_bad_exit) + + @property + def kill_wait_time(self): + return u16_parse(self.ptr.kill_wait) + + @property + def launch_parameters(self): + return cstr.to_list(self.ptr.launch_params) + + @property + def licenses(self): + return cstr.to_dict(self.ptr.licenses, delim1=",", + delim2=":", def_value=1) + + @property + def log_time_format(self): + flag = self.ptr.log_fmt + if flag == slurm.LOG_FMT_ISO8601_MS: + return "iso8601_ms" + elif flag == slurm.LOG_FMT_ISO8601: + return "iso8601" + elif flag == slurm.LOG_FMT_RFC5424_MS: + return "rfc5424_ms" + elif flag == slurm.LOG_FMT_RFC5424: + return "rfc5424" + elif flag == slurm.LOG_FMT_CLOCK: + return "clock" + elif flag == slurm.LOG_FMT_SHORT: + return "short" + elif flag == slurm.LOG_FMT_THREAD_ID: + return "thread_id" + elif flag == slurm.LOG_FMT_RFC3339: + return "rfc3339" + else: + return None + + @property + def mail_domain(self): + return cstr.to_unicode(self.ptr.mail_domain) + + @property + def mail_program(self): + return cstr.to_unicode(self.ptr.mail_prog) + + @property + def max_array_size(self): + return u32_parse(self.ptr.max_array_sz) + + @property + def max_batch_requeue(self): + return u32_parse(self.ptr.max_batch_requeue) + + @property + def max_dbd_msgs(self): + return u32_parse(self.ptr.max_dbd_msgs) + + @property + def max_job_count(self): + return u32_parse(self.ptr.max_job_cnt) + + @property + def max_job_id(self): + return u32_parse(self.ptr.max_job_id) + + @property + def max_memory_per_cpu(self): + return _get_memory(self.ptr.max_mem_per_cpu, per_cpu=True) + + @property + def max_memory_per_node(self): + return _get_memory(self.ptr.max_mem_per_cpu, per_cpu=False) + + @property + def max_node_count(self): + return u32_parse(self.ptr.max_node_cnt) + + @property + def max_step_count(self): + return u32_parse(self.ptr.max_step_cnt) + + @property + def max_tasks_per_node(self): + return u32_parse(self.ptr.max_tasks_per_node) + + @property + def mcs_plugin(self): + return cstr.to_unicode(self.ptr.mcs_plugin) + + @property + def mcs_parameters(self): + return cstr.to_list(self.ptr.mcs_plugin_params) + + @property + def min_job_age(self): + return u32_parse(self.ptr.min_job_age) + + @property + def mpi_default(self): + return cstr.to_unicode(self.ptr.mpi_default) + + @property + def mpi_parameters(self): + return cstr.to_dict(self.ptr.mpi_params, delim1=",", + delim2="=", def_value=True) + + @property + def message_timeout(self): + return u16_parse(self.ptr.msg_timeout) + + @property + def next_job_id(self): + return u32_parse(self.ptr.next_job_id) + + @property + def node_features_plugins(self): + return cstr.to_list(self.ptr.node_features_plugins) + + @property + def over_time_limit(self): + return u16_parse(self.ptr.over_time_limit) + + @property + def plugin_dirs(self): + return cstr.to_list(self.ptr.plugindir, default=None, delim=":") + + @property + def plugin_stack_config(self): + return cstr.to_unicode(self.ptr.plugstack) + + @property + def preempt_exempt_time(self): + # seconds? + return _raw_time(self.ptr.preempt_exempt_time) + + @property + def preempt_mode(self): + cdef char *tmp = slurm_preempt_mode_string(self.ptr.preempt_mode) + return cstr.to_unicode(tmp) + + @property + def preempt_parameters(self): + return cstr.to_dict(self.ptr.preempt_params, delim1=",", + delim2="=", def_value=True) + + @property + def preempt_type(self): + return cstr.to_unicode(self.ptr.preempt_type) + + @property + def prep_parameters(self): + return cstr.to_list(self.ptr.prep_params) + + @property + def prep_plugins(self): + return cstr.to_list(self.ptr.prep_plugins) + + @property + def priority_decay_half_life(self): + return u32_parse(self.ptr.priority_decay_hl) + + @property + def priority_calc_period(self): + return u32_parse(self.ptr.priority_calc_period) + + @property + def priority_favor_small(self): + return u16_parse_bool(self.ptr.priority_favor_small) + + @property + def priority_flags(self): + cdef char *data = slurm.priority_flags_string(self.ptr.priority_flags) + return cstr.to_list_free(&data) + + @property + def priority_max_age(self): + return u32_parse(self.ptr.priority_max_age) + + @property + def priority_parameters(self): + return cstr.to_unicode(self.ptr.priority_params) + + @property + def priority_usage_reset_period(self): + flag = self.ptr.priority_reset_period + if flag == slurm.PRIORITY_RESET_NONE: + return None + elif flag == slurm.PRIORITY_RESET_NOW: + return "NOW" + elif flag == slurm.PRIORITY_RESET_DAILY: + return "DAILY" + elif flag == slurm.PRIORITY_RESET_WEEKLY: + return "WEEKLY" + elif flag == slurm.PRIORITY_RESET_MONTHLY: + return "MONTHLY" + elif flag == slurm.PRIORITY_RESET_QUARTERLY: + return "QUARTERLY" + elif flag == slurm.PRIORITY_RESET_YEARLY: + return "YEARLY" + else: + return None + + @property + def priority_type(self): + return cstr.to_unicode(self.ptr.priority_type) + + @property + def priority_weight_age(self): + return u32_parse(self.ptr.priority_weight_age, zero_is_noval=False) + + @property + def priority_weight_assoc(self): + return u32_parse(self.ptr.priority_weight_assoc, zero_is_noval=False) + + @property + def priority_weight_fair_share(self): + return u32_parse(self.ptr.priority_weight_fs, zero_is_noval=False) + + @property + def priority_weight_job_size(self): + return u32_parse(self.ptr.priority_weight_js, zero_is_noval=False) + + @property + def priority_weight_partition(self): + return u32_parse(self.ptr.priority_weight_part, zero_is_noval=False) + + @property + def priority_weight_qos(self): + return u32_parse(self.ptr.priority_weight_qos, zero_is_noval=False) + + @property + def priority_weight_tres(self): + return cstr.to_dict(self.ptr.priority_weight_tres) + + @property + def private_data(self): + cdef char tmp[128] + slurm.private_data_string(self.ptr.private_data, tmp, sizeof(tmp)) + out = cstr.to_unicode(tmp) + if not out or out == "none": + return [] + + return out.split(",") + + @property + def proctrack_type(self): + return cstr.to_unicode(self.ptr.proctrack_type) + + @property + def prolog(self): + return cstr.to_list_with_count(self.ptr.prolog, + self.ptr.prolog_cnt) + + @property + def prolog_epilog_timeout(self): + return u16_parse(self.ptr.prolog_epilog_timeout) + + @property + def prolog_slurmctld(self): + return cstr.to_list_with_count(self.ptr.prolog_slurmctld, + self.ptr.prolog_slurmctld_cnt) + + @property + def propagate_prio_process(self): + return u16_parse(self.ptr.propagate_prio_process, zero_is_noval=False) + + @property + def prolog_flags(self): + cdef char *data = slurm.prolog_flags2str(self.ptr.prolog_flags) + return cstr.to_list_free(&data) + + @property + def propagate_resource_limits(self): + return cstr.to_list(self.ptr.propagate_rlimits) + + @property + def propagate_resource_limits_except(self): + return cstr.to_list(self.ptr.propagate_rlimits_except) + + @property + def reboot_program(self): + return cstr.to_unicode(self.ptr.reboot_program) + + @property + def reconfig_flags(self): + cdef char *tmp = slurm.reconfig_flags2str(self.ptr.reconfig_flags) + return cstr.to_list_free(&tmp) + + @property + def requeue_exit(self): + return cstr.to_unicode(self.ptr.requeue_exit) + + @property + def requeue_exit_hold(self): + return cstr.to_unicode(self.ptr.requeue_exit_hold) + + @property + def resume_fail_program(self): + return cstr.to_unicode(self.ptr.resume_fail_program) + + @property + def resume_program(self): + return cstr.to_unicode(self.ptr.resume_program) + + @property + def resume_rate(self): + return u16_parse(self.ptr.resume_rate) + + @property + def resume_timeout(self): + return u16_parse(self.ptr.resume_timeout) + + @property + def reservation_epilog(self): + return cstr.to_unicode(self.ptr.resv_epilog) + + @property + def reservation_over_run(self): + return u16_parse(self.ptr.resv_over_run, zero_is_noval=False) + + @property + def reservation_prolog(self): + return cstr.to_unicode(self.ptr.resv_prolog) + + @property + def return_to_service(self): + return u16_parse(self.ptr.ret2service, zero_is_noval=False) + + @property + def scheduler_log_file(self): + return cstr.to_unicode(self.ptr.sched_logfile) + + @property + def scheduler_logging_enabled(self): + return u16_parse_bool(self.ptr.sched_log_level) + + @property + def scheduler_parameters(self): + return cstr.to_dict(self.ptr.sched_params, delim1=",", + delim2="=", def_value=True) + + @property + def scheduler_time_slice(self): + return u16_parse(self.ptr.sched_time_slice) + + @property + def scheduler_type(self): + return cstr.to_unicode(self.ptr.schedtype) + + @property + def scron_parameters(self): + return cstr.to_list(self.ptr.scron_params) + + @property + def select_type(self): + return cstr.to_unicode(self.ptr.select_type) + + @property + def select_type_parameters(self): + cdef char *tmp = slurm.select_type_param_string(self.ptr.select_type_param) + return cstr.to_list(tmp) + + @property + def priority_site_factor_plugin(self): + return cstr.to_unicode(self.ptr.site_factor_plugin) + + @property + def priority_site_factor_parameters(self): + return cstr.to_unicode(self.ptr.site_factor_params) + + @property + def slurm_conf_path(self): + return cstr.to_unicode(self.ptr.slurm_conf) + + @property + def slurm_user_id(self): + return self.ptr.slurm_user_id + + @property + def slurm_user_name(self): + return cstr.to_unicode(self.ptr.slurm_user_name) + + @property + def slurmd_user_id(self): + return self.ptr.slurm_user_id + + @property + def slurmd_user_name(self): + return cstr.to_unicode(self.ptr.slurmd_user_name) + + @property + def slurmctld_log_level(self): + return _log_level_int_to_str(self.ptr.slurmctld_debug) + + @property + def slurmctld_log_file(self): + return cstr.to_unicode(self.ptr.slurmctld_logfile) + + @property + def slurmctld_pid_file(self): + return cstr.to_unicode(self.ptr.slurmctld_pidfile) + + @property + def slurmctld_port(self): + port = self.ptr.slurmctld_port + if self.ptr.slurmctld_port_count > 1: + # Slurmctld port can be a range actually, calculated by using the + # number of ports in use that slurm conf reports for slurmctld + last_port = port + self.ptr.slurmctld_port_count - 1 + port = f"{port}-{last_port}" + + return str(port) + + @property + def slurmctld_primary_off_program(self): + return cstr.to_unicode(self.ptr.slurmctld_primary_off_prog) + + @property + def slurmctld_primary_on_program(self): + return cstr.to_unicode(self.ptr.slurmctld_primary_on_prog) + + @property + def slurmctld_syslog_level(self): + return _log_level_int_to_str(self.ptr.slurmctld_syslog_debug) + + @property + def slurmctld_timeout(self): + return u16_parse(self.ptr.slurmctld_timeout) + + @property + def slurmctld_parameters(self): + return cstr.to_dict(self.ptr.slurmctld_params, delim1=",", + delim2="=", def_value=True) + + @property + def slurmd_log_level(self): + return _log_level_int_to_str(self.ptr.slurmd_debug) + + @property + def slurmd_log_file(self): + return cstr.to_unicode(self.ptr.slurmd_logfile) + + @property + def slurmd_parameters(self): + return cstr.to_dict(self.ptr.slurmd_params, delim1=",", + delim2="=", def_value=True) + + @property + def slurmd_pid_file(self): + return cstr.to_unicode(self.ptr.slurmd_pidfile) + + @property + def slurmd_port(self): + return self.ptr.slurmd_port + + @property + def slurmd_spool_directory(self): + return cstr.to_unicode(self.ptr.slurmd_spooldir) + + @property + def slurmd_syslog_level(self): + return _log_level_int_to_str(self.ptr.slurmd_syslog_debug) + + @property + def slurmd_timeout(self): + return u16_parse(self.ptr.slurmd_timeout) + + @property + def srun_epilog(self): + return cstr.to_unicode(self.ptr.srun_epilog) + + @property + def srun_port_range(self): + if not self.ptr.srun_port_range: + return None + + low = self.ptr.srun_port_range[0] + high = self.ptr.srun_port_range[1] + return f"{low}-{high}" + + @property + def srun_prolog(self): + return cstr.to_unicode(self.ptr.srun_prolog) + + @property + def state_save_location(self): + return cstr.to_unicode(self.ptr.state_save_location) + + @property + def suspend_exclude_nodes(self): + return cstr.to_unicode(self.ptr.suspend_exc_nodes) + + @property + def suspend_exclude_partitions(self): + return cstr.to_list(self.ptr.suspend_exc_parts) + + @property + def suspend_exclude_states(self): + return cstr.to_list(self.ptr.suspend_exc_states) + + @property + def suspend_program(self): + return cstr.to_unicode(self.ptr.suspend_program) + + @property + def suspend_rate(self): + return u16_parse(self.ptr.suspend_rate) + + @property + def suspend_time(self): + return u32_parse(self.ptr.suspend_time) + + @property + def suspend_timeout(self): + return u16_parse(self.ptr.suspend_timeout) + + @property + def switch_type(self): + return cstr.to_unicode(self.ptr.switch_type) + + @property + def switch_parameters(self): + return cstr.to_dict(self.ptr.switch_param, delim1=",", + delim2="=", def_value=True) + + @property + def task_epilog(self): + return cstr.to_unicode(self.ptr.task_epilog) + + @property + def task_plugin(self): + return cstr.to_unicode(self.ptr.task_plugin) + + @property + def task_plugin_parameters(self): + cdef char cpu_bind[256] + slurm_sprint_cpu_bind_type(cpu_bind, + self.ptr.task_plugin_param) + if cpu_bind == "(null type)": + return [] + + return cstr.to_list(cpu_bind) + + @property + def task_prolog(self): + return cstr.to_unicode(self.ptr.task_prolog) + + @property + def tls_parameters(self): + return cstr.to_list(self.ptr.tls_params) + + @property + def tls_type(self): + return cstr.to_unicode(self.ptr.tls_type) + + @property + def tcp_timeout(self): + return u16_parse(self.ptr.tcp_timeout) + + @property + def temporary_filesystem(self): + return cstr.to_unicode(self.ptr.tmp_fs) + + @property + def topology_parameters(self): + return cstr.to_dict(self.ptr.topology_param, delim1=",", + delim2="=", def_value=True) + + @property + def topology_plugin(self): + return cstr.to_unicode(self.ptr.topology_plugin) + + @property + def tree_width(self): + return u16_parse(self.ptr.tree_width) + + @property + def unkillable_step_program(self): + return cstr.to_unicode(self.ptr.unkillable_program) + + @property + def unkillable_step_timeout(self): + return u16_parse(self.ptr.unkillable_timeout) + + @property + def track_wckey(self): + if self.ptr.conf_flags & slurm.CONF_FLAG_WCKEY: + return True + return False + + @property + def use_pam(self): + if self.ptr.conf_flags & slurm.CONF_FLAG_PAM: + return True + return False + + @property + def version(self): + return cstr.to_unicode(self.ptr.version) + + @property + def virtual_memory_size_factor(self): + return u16_parse(self.ptr.vsize_factor) + + @property + def wait_time(self): + return u16_parse(self.ptr.wait_time) + + @property + def x11_parameters(self): + return cstr.to_list(self.ptr.x11_params) + + +cdef dict _parse_config_key_pairs(void *ptr, owned=False): + cdef: + SlurmList conf = SlurmList.wrap(ptr, owned=owned) + SlurmListItem item + config_key_pair_t *key_pair + dict out = {} + + for item in conf: + key_pair = item.data + name = cstr.to_unicode(key_pair.name) + val = cstr.to_unicode(key_pair.value) + out[name] = val + + return out + + +def _str_to_bool(val, true_str, false_str): + if not val: + return False + + v = val.lower() + if v == true_str: + return True + elif v == false_str: + return False + else: + return False + + +def _yesno_to_bool(val): + return _str_to_bool(val, "yes", "no") + + +def _true_false_to_bool(val): + return _str_to_bool(val, "true", "false") + + +def _log_level_int_to_str(flags): + data = cstr.to_unicode(slurm.log_num2string(flags)) + if data == "(null)": + return None + else: + return data + + +def _get_memory(value, per_cpu): + if value != slurm.NO_VAL64: + if value & slurm.MEM_PER_CPU and per_cpu: + if value == slurm.MEM_PER_CPU: + return UNLIMITED + return u64_parse(value & (~slurm.MEM_PER_CPU)) + + # For these values, Slurm interprets 0 as being equal to + # INFINITE/UNLIMITED + elif value == 0 and not per_cpu: + return UNLIMITED + + elif not value & slurm.MEM_PER_CPU and not per_cpu: + return u64_parse(value) + + return None diff --git a/pyslurm/core/slurmctld.pxd b/pyslurm/core/slurmctld/enums.pyx similarity index 60% rename from pyslurm/core/slurmctld.pxd rename to pyslurm/core/slurmctld/enums.pyx index 8bafb01f..e2fe7cf7 100644 --- a/pyslurm/core/slurmctld.pxd +++ b/pyslurm/core/slurmctld/enums.pyx @@ -1,7 +1,7 @@ ######################################################################### -# slurmctld.pxd - pyslurm slurmctld api +# slurmctld/enums.pyx - pyslurm slurmctld enums ######################################################################### -# Copyright (C) 2023 Toni Harzendorf +# Copyright (C) 2025 Toni Harzendorf # # This file is part of PySlurm # @@ -22,18 +22,17 @@ # cython: c_string_type=unicode, c_string_encoding=default # cython: language_level=3 -from pyslurm cimport slurm -from pyslurm.slurm cimport ( - slurm_conf_t, - slurm_load_ctl_conf, - slurm_free_ctl_conf, - slurm_preempt_mode_string, - try_xmalloc, -) -from pyslurm.utils cimport cstr -from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, int64_t -from pyslurm.utils.uint cimport * +from enum import IntEnum +class ShutdownMode(IntEnum): + """Mode of operation for shutdown action.""" + ALL = 0 + CORE_FILE = 1 + CONTROLLER_ONLY = 2 -cdef class Config: - cdef slurm_conf_t *ptr + +# A bit hacky, but it works for now. Putting the docstring under the enum value +# does not work unfortunately. +ShutdownMode.ALL.__doc__ = "Shutdown all daemons (`slurmctld` and `slurmd`)" +ShutdownMode.CORE_FILE.__doc__ = "Shutdown only `slurmctld`, and create a coredump" +ShutdownMode.CONTROLLER_ONLY.__doc__ = "Shutdown only `slurmctld`, without a coredump" diff --git a/pyslurm/db/step.pyx b/pyslurm/db/step.pyx index 0faf4d79..11cc1875 100644 --- a/pyslurm/db/step.pyx +++ b/pyslurm/db/step.pyx @@ -34,8 +34,8 @@ from pyslurm.utils.helpers import ( instance_to_dict, _get_exit_code, humanize_step_id, + cpu_freq_int_to_str, ) -from pyslurm.core.job.util import cpu_freq_int_to_str cdef class JobSteps(dict): diff --git a/pyslurm/settings.pyx b/pyslurm/settings.pyx index 5085a9f5..36b834c2 100644 --- a/pyslurm/settings.pyx +++ b/pyslurm/settings.pyx @@ -30,4 +30,4 @@ from pyslurm.utils cimport cstr LOCAL_CLUSTER = cstr.to_unicode(slurm.slurm_conf.cluster_name) if not LOCAL_CLUSTER: slurm_conf = slurmctld.Config.load() - LOCAL_CLUSTER = slurm_conf.cluster + LOCAL_CLUSTER = slurm_conf.cluster_name diff --git a/pyslurm/slurm/extra.pxi b/pyslurm/slurm/extra.pxi index bab83890..09dfae70 100644 --- a/pyslurm/slurm/extra.pxi +++ b/pyslurm/slurm/extra.pxi @@ -259,6 +259,7 @@ cdef extern int slurm_addto_step_list(list_t *step_list, char *names) cdef extern int slurmdb_report_set_start_end_time(time_t *start, time_t *end) cdef extern uint16_t slurm_get_track_wckey() cdef extern void slurm_sprint_cpu_bind_type(char *str, cpu_bind_type_t cpu_bind_type) +cdef extern void slurm_accounting_enforce_string(uint16_t enforce, char *str, int str_len) # Slurm bit functions @@ -300,3 +301,22 @@ cdef extern void slurmdb_init_tres_cond(slurmdb_tres_cond_t *tres, bool free_it) cdef extern void slurm_free_update_part_msg(update_part_msg_t *msg) cdef extern void slurm_free_partition_info_members(partition_info_t *node) + +# +# Slurmctld stuff +# + +cdef extern char *debug_flags2str(uint64_t debug_flags) +cdef extern int debug_str2flags(const char* debug_flags, uint64_t *flags_out) +cdef extern char *parse_part_enforce_type_2str(uint16_t type) +cdef extern char *health_check_node_state_str(uint32_t node_state) +cdef extern char *priority_flags_string(uint16_t priority_flags) +cdef extern char* prolog_flags2str(uint16_t prolog_flags) +cdef extern uint16_t prolog_str2flags(char *prolog_flags) +cdef extern char *log_num2string(uint16_t inx) +cdef extern uint16_t log_string2num(const char *name) +cdef extern char *private_data_string(uint16_t private_data, char *str, int str_len) +cdef extern char *reconfig_flags2str(uint16_t reconfig_flags) +cdef extern uint16_t reconfig_str2flags(char *reconfig_flags) +cdef extern char *select_type_param_string(uint16_t select_type_param) +cdef extern char *job_defaults_str(list_t *in_list) diff --git a/pyslurm/utils/cstr.pxd b/pyslurm/utils/cstr.pxd index e8014a5f..0579819a 100644 --- a/pyslurm/utils/cstr.pxd +++ b/pyslurm/utils/cstr.pxd @@ -31,9 +31,11 @@ cdef to_unicode(char *s, default=*) cdef fmalloc(char **old, val) cdef fmalloc2(char **p1, char **p2, val) cdef free_array(char **arr, count) -cpdef list to_list(char *str_list, default=*) +cpdef list to_list(char *str_list, default=*, delim=*) +cdef list to_list_free(char **str_list) +cdef list to_list_with_count(char **str_list, cnt) cdef from_list(char **old, vals, delim=*) cdef from_list2(char **p1, char **p2, vals, delim=*) -cpdef dict to_dict(char *str_dict, str delim1=*, str delim2=*) +cpdef dict to_dict(char *str_dict, str delim1=*, str delim2=*, def_value=*) cdef from_dict(char **old, vals, prepend=*, str delim1=*, str delim2=*) cpdef dict to_gres_dict(char *gres) diff --git a/pyslurm/utils/cstr.pyx b/pyslurm/utils/cstr.pyx index 412ef5d3..ad7e1015 100644 --- a/pyslurm/utils/cstr.pyx +++ b/pyslurm/utils/cstr.pyx @@ -96,14 +96,30 @@ cdef fmalloc(char **old, val): old[0] = NULL -cpdef list to_list(char *str_list, default=[]): +cpdef list to_list(char *str_list, default=None, delim=","): """Convert C-String to a list.""" cdef str ret = to_unicode(str_list) if not ret: - return default + return [] if default is None else default + + return ret.split(delim) + + +cdef list to_list_free(char **str_list): + out = to_list(str_list[0]) + xfree(str_list[0]) + return out - return ret.split(",") + +cdef list to_list_with_count(char **str_list, cnt): + cdef list out = [] + + if cnt and cnt != slurm.NO_VAL: + for i in range(cnt): + out.append(to_unicode(str_list[i])) + + return out def list_to_str(vals, delim=","): @@ -125,7 +141,8 @@ cdef from_list2(char **p1, char **p2, vals, delim=","): from_list(p2, vals, delim) -cpdef dict to_dict(char *str_dict, str delim1=",", str delim2="="): +cpdef dict to_dict(char *str_dict, str delim1=",", str delim2="=", + def_value=None): """Convert a char* key=value pair to dict. With a char* Slurm represents key-values pairs usually in the form of: @@ -144,6 +161,8 @@ cpdef dict to_dict(char *str_dict, str delim1=",", str delim2="="): if delim2 in kv: key, val = kv.split(delim2, 1) out[key] = int(val) if val.isdigit() else val + elif def_value is not None: + out[kv] = def_value return out diff --git a/pyslurm/utils/helpers.pyx b/pyslurm/utils/helpers.pyx index 577a1c9a..bac5f5f7 100644 --- a/pyslurm/utils/helpers.pyx +++ b/pyslurm/utils/helpers.pyx @@ -387,3 +387,34 @@ cpdef gres_from_tres_dict(dict tres_dict): for k, v in tres_dict.items() if gres_prefix in k } + + +def cpu_freq_int_to_str(freq): + """Convert a numerical cpufreq value to its string representation.""" + if freq == slurm.CPU_FREQ_LOW: + return "LOW" + elif freq == slurm.CPU_FREQ_MEDIUM: + return "MEDIUM" + elif freq == slurm.CPU_FREQ_HIGHM1: + return "HIGHM1" + elif freq == slurm.CPU_FREQ_HIGH: + return "HIGH" + elif freq == slurm.CPU_FREQ_CONSERVATIVE: + return "CONSERVATIVE" + elif freq == slurm.CPU_FREQ_PERFORMANCE: + return "PERFORMANCE" + elif freq == slurm.CPU_FREQ_POWERSAVE: + return "POWERSAVE" + elif freq == slurm.CPU_FREQ_USERSPACE: + return "USERSPACE" + elif freq == slurm.CPU_FREQ_ONDEMAND: + return "ONDEMAND" + elif freq == slurm.CPU_FREQ_SCHEDUTIL: + return "SCHEDUTIL" + elif freq & slurm.CPU_FREQ_RANGE_FLAG: + return None + elif freq == slurm.NO_VAL or freq == 0: + return None + else: + # This is in kHz + return freq diff --git a/scripts/builddocs.sh b/scripts/builddocs.sh index 9b1aeaea..23e37ce3 100755 --- a/scripts/builddocs.sh +++ b/scripts/builddocs.sh @@ -17,7 +17,6 @@ done shift $((OPTIND-1)) -python setup.py clean pip install -r doc_requirements.txt scripts/build.sh -j${OPT_JOBS} -d mkdocs build diff --git a/scripts/griffe_exts.py b/scripts/griffe_exts.py new file mode 100644 index 00000000..905f8358 --- /dev/null +++ b/scripts/griffe_exts.py @@ -0,0 +1,106 @@ +######################################################################### +# scripts/griffe_exts.py - griffe extensions for documentation +######################################################################### +# Copyright (C) 2025 Toni Harzendorf +# +# This file is part of PySlurm +# +# PySlurm is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# PySlurm is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with PySlurm; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import ast +import inspect +import griffe +import pyslurm +import re + +logger = griffe.get_logger(__name__) +SLURM_VERSION = ".".join(pyslurm.__version__.split(".")[:-1]) +SLURM_DOCS_URL_BASE = "https://slurm.schedmd.com/archive" +SLURM_DOCS_URL_VERSIONED = f"{SLURM_DOCS_URL_BASE}/slurm-{SLURM_VERSION}-latest" + +config_files = ["acct_gather.conf", "slurm.conf", "cgroup.conf", "mpi.conf"] + + +def replace_with_slurm_docs_url(match): + first_part = match.group(1) + second_part = match.group(2) + ref = f"[{first_part}{second_part}]" + return f'{ref}({SLURM_DOCS_URL_VERSIONED}/{first_part}.html{second_part})' + + +pattern = re.compile( + r'\{(' + + '|'.join([re.escape(config) for config in config_files]) + + r')' # Match the first word before "#" + + r'([#][^}]+)\}' # Match "#" and everything after it until } +) + +# This class is inspired from here, with a few adaptions: +# https://github.com/mkdocstrings/griffe/blob/97f3613c5f0ae5653e8b91479c716b9ec44baacc/docs/guide/users/extending.md#full-example +# +# ISC License +# +# Copyright (c) 2021, Timothée Mazzucotelli +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +class DynamicDocstrings(griffe.Extension): + def __init__(self, include_paths: list[str] | None = None, + ignore_paths: list[str] | None = None) -> None: + + self.include_paths = include_paths + self.ignore_paths = ignore_paths + + def on_instance( + self, + node: ast.AST | griffe.ObjectNode, + obj: griffe.Object, + agent: griffe.Visitor | griffe.Inspector, + **kwargs, + ) -> None: + + if ((self.include_paths and obj.path not in self.include_paths) + or (self.ignore_paths and obj.path in self.ignore_paths)): + return + + try: + runtime_obj = griffe.dynamic_import(obj.path) + docstring = runtime_obj.__doc__ + except ImportError: + logger.debug(f"Could not get dynamic docstring for {obj.path}") + return + except AttributeError: + logger.debug(f"Object {obj.path} does not have a __doc__ attribute") + return + + if not docstring or not obj.docstring: + return + + fmt_docstring = pattern.sub(replace_with_slurm_docs_url, docstring) + if fmt_docstring == docstring: + # No need to update the docstring if nothing has changed + return + + docstring = inspect.cleandoc(fmt_docstring) + obj.docstring.value = docstring diff --git a/tests/integration/test_slurmctld.py b/tests/integration/test_slurmctld.py new file mode 100644 index 00000000..4d1f1004 --- /dev/null +++ b/tests/integration/test_slurmctld.py @@ -0,0 +1,109 @@ +######################################################################### +# test_slurmctld.py - slurmctld integration tests +######################################################################### +# Copyright (C) 2025 Toni Harzendorf +# +# This file is part of PySlurm +# +# PySlurm is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# PySlurm is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with PySlurm; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""test_slurmctld.py - integration test basic slurmctld functionalities.""" + +import pytest +import pyslurm +from pyslurm import slurmctld + + +def test_ping(): + resp = slurmctld.ping_primary() + assert resp.is_responding + assert resp.is_primary + assert resp.index == 0 + assert resp.hostname is not None + assert resp.latency > 0 + assert resp.to_dict() + + +def test_ping_all(): + pings = slurmctld.ping_all() + assert isinstance(pings, list) + + for resp in pings: + assert resp.hostname is not None + assert resp.latency > 0 + + +def test_reconfigure(): + slurmctld.reconfigure() + + +def test_load_config(): + conf = slurmctld.Config.load() + + assert conf + assert conf.to_dict() + assert conf.cgroup_config + assert conf.accounting_gather_config + assert conf.mpi_config + + +def test_debug_flags(): + slurmctld.clear_debug_flags() + + slurmctld.add_debug_flags([]) + assert slurmctld.get_debug_flags() == [] + + slurmctld.add_debug_flags(["CpuFrequency", "Backfill"]) + assert slurmctld.get_debug_flags() == ["Backfill", "CpuFrequency"] + + slurmctld.add_debug_flags(["Agent"]) + assert slurmctld.get_debug_flags() == ["Agent", "Backfill", "CpuFrequency"] + + slurmctld.remove_debug_flags(["CpuFrequency"]) + assert slurmctld.get_debug_flags() == ["Agent", "Backfill"] + + slurmctld.clear_debug_flags() + assert slurmctld.get_debug_flags() == [] + + +def test_log_level(): + slurmctld.set_log_level("debug5") + assert slurmctld.get_log_level() == "debug5" + + slurmctld.set_log_level("debug2") + assert slurmctld.get_log_level() == "debug2" + + with pytest.raises(pyslurm.RPCError, + match=r"Invalid Log*"): + slurmctld.set_log_level("invalid") + + +def test_scheduler_log_level(): + assert not slurmctld.is_scheduler_logging_enabled() + + +def test_fair_share_dampening_factor(): + slurmctld.set_fair_share_dampening_factor(100) + assert slurmctld.get_fair_share_dampening_factor() == 100 + + slurmctld.set_fair_share_dampening_factor(1) + assert slurmctld.get_fair_share_dampening_factor() == 1 + + with pytest.raises(pyslurm.RPCError, + match=r"Invalid Dampening*"): + slurmctld.set_fair_share_dampening_factor(0) + + with pytest.raises(pyslurm.RPCError, + match=r"Invalid Dampening*"): + slurmctld.set_fair_share_dampening_factor(99999999) diff --git a/tests/unit/test_common.py b/tests/unit/test_common.py index 6bc3f708..6f50cf45 100644 --- a/tests/unit/test_common.py +++ b/tests/unit/test_common.py @@ -1,7 +1,7 @@ ######################################################################### # test_common.py - common utility tests ######################################################################### -# Copyright (C) 2023 Toni Harzendorf +# Copyright (C) 2025 Toni Harzendorf # # This file is part of PySlurm # @@ -108,6 +108,25 @@ def test_str_to_dict(self): assert cstr.to_dict(input_str) == expected_dict assert cstr.to_dict("") == {} + expected_dict = {"param1": True, "param2": "opt1", "param3": True} + input_str = "param1,param2=opt1,param3" + assert cstr.to_dict(input_str, delim1=",", delim2="=", def_value=True) + + expected_dict = {"license1": 1, "license2": 5, "license3": 20} + input_str = "license1,license2:5,license3:20" + assert cstr.to_dict(input_str, delim1=",", delim2=":", def_value=1) + + def test_str_to_list(self): + expected_list = ["val1", "val2", "val3"] + input_str = "val1,val2,val3" + assert cstr.to_list(input_str) == expected_list + + expected_list = ["/path/to/dir1", "/path/to/dir2"] + input_str = "/path/to/dir1:/path/to/dir2" + assert cstr.to_list(input_str, default=None, delim=":") == expected_list + + assert cstr.to_list("") == [] + def test_dict_to_str(self): input_dict = {"key1": "value1", "key2": "value2"} expected_str = "key1=value1,key2=value2" @@ -255,32 +274,6 @@ def test_set_parse_bool_flag(self): assert not part.allow_root_jobs -# def _uint_bool_impl(self, arg): -# js = JobSubmitDescription() - -# setattr(js, arg, True) -# assert getattr(js, arg) == True - -# setattr(js, arg, False) -# assert getattr(js, arg) == False - -# # Set to true again to make sure toggling actually works. -# setattr(js, arg, True) -# assert getattr(js, arg) == True - -# setattr(js, arg, None) -# assert getattr(js, arg) == False - -# def test_u8_bool(self): -# self._uint_bool_impl("overcommit") - -# def test_u16_bool(self): -# self._uint_bool_impl("requires_contiguous_nodes") - -# def test_u64_bool_flag(self): -# self._uint_bool_impl("kill_on_invalid_dependency") - - class TestTime: def test_parse_minutes(self): diff --git a/tests/unit/test_job.py b/tests/unit/test_job.py index 7ce7f05b..5bb58b6e 100644 --- a/tests/unit/test_job.py +++ b/tests/unit/test_job.py @@ -24,6 +24,7 @@ import pyslurm from pyslurm import Job from pyslurm.core.job.util import * +from pyslurm.utils.helpers import cpu_freq_int_to_str def test_create_instance(): job = Job(9999) diff --git a/tests/unit/test_partition.py b/tests/unit/test_partition.py index b699893c..490fba1b 100644 --- a/tests/unit/test_partition.py +++ b/tests/unit/test_partition.py @@ -32,7 +32,10 @@ def test_create_instance(): def test_parse_all(): - assert Partition("normal").to_dict() + part = Partition("normal") + assert part.to_dict() + assert part.allowed_submit_nodes == ["ALL"] + assert part.allowed_accounts == ["ALL"] def test_parse_memory():