diff --git a/.github/workflows/pyslurm.yml b/.github/workflows/pyslurm.yml index 9bc2f025..e44cf0af 100644 --- a/.github/workflows/pyslurm.yml +++ b/.github/workflows/pyslurm.yml @@ -1,7 +1,7 @@ name: PySlurm env: - SLURM_DOCKER_IMAGE: giovtorres/slurm-docker:25.11.4-rl10 + SLURM_DOCKER_IMAGE: giovtorres/slurm-docker:26.05.3-rl10 on: push: @@ -18,11 +18,11 @@ jobs: strategy: matrix: include: - - slurm-image: giovtorres/slurm-docker:25.11.4-rl10 + - slurm-image: giovtorres/slurm-docker:26.05.3-rl10 python-version: "3.12" - - slurm-image: giovtorres/slurm-docker:25.11.4-rl9 + - slurm-image: giovtorres/slurm-docker:26.05.3-rl9 python-version: "3.9" - - slurm-image: giovtorres/slurm-docker:25.11.4-rl8 + - slurm-image: giovtorres/slurm-docker:26.05.3-rl8 python-version: "3.6" fail-fast: false env: diff --git a/CHANGELOG.md b/CHANGELOG.md index f8d22b1d..cbaf7951 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,52 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased on the [26.05.x](https://github.com/PySlurm/pyslurm/tree/26.05.x) branch + +### Added + +- Support for Slurm 26.05.x +- New Enums, both available directly as `pyslurm.`: + - `JobExclusive` + - `JobOversubscribe` +- New members added for the `pyslurm.Job` class: + - `container_type` + - `exclusive` + - `oversubscribe` + - `memory_update_delay` + - `memory_update_margin` +- New member added for the `pyslurm.JobStep` class: + - `container_type` +- New member added for the `pyslurm.JobSubmitDescription` class: + - `container_type` (also recognized as `--container-type` in batch scripts) +- New member added for the `pyslurm.Node` class: + - `suspend_time` +- New members added for the `pyslurm.slurmctld.Config` class: + - `health_check_timeout` + - `license_parameters` + - `metrics_auth` + - `metrics_auth_users` + - `metrics_parameters` + - `slurmctld_http_auth_parameters` + - `slurmd_http_auth_parameters` + +### Fixed + +- Fixed heap corruption in `pyslurm.Reservation.create()`. Slurm 26.05 changed + `slurm_create_reservation` to return an `xmalloc`-allocated name, which must + be released with `xfree` rather than `free`. +- Added the `node_ranks` member to the internal `job_resources` struct + definition. Its absence shifted every following member, corrupting + `pyslurm.Job.cpus`. + +### Changed + +- Slurm 26.05 changed a number of RPCs to take a `slurm_step_id_t` instead of a + plain `uint32_t` job id. This is handled internally and the PySlurm API is + unchanged. +- `ESLURM_ERROR_ON_DESC_TO_RECORD_COPY` was removed in Slurm 26.05 and is + replaced by `ESLURM_MAX_JOB_COUNT`. + ## Unreleased on the [25.11.x](https://github.com/PySlurm/pyslurm/tree/25.11.x) branch ### Added diff --git a/README.md b/README.md index ab37420a..f692c57d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ pyslurm is the Python client library for the [Slurm Workload Manager](https://sl ## Requirements -* [Slurm](https://slurm.schedmd.com) 25.11.x — shared library and header files +* [Slurm](https://slurm.schedmd.com) 26.05.x — shared library and header files * [Python](https://www.python.org) >= 3.6 ## Versioning @@ -15,6 +15,7 @@ major release. | PySlurm | Slurm | |---|---| +| 26.05.x | 26.05.x | | 25.11.x | 25.11.x | | 24.05.x | 24.05.x | | 23.11.x | 23.11.x | diff --git a/UPGRADE_C_API.rst b/UPGRADE_C_API.rst index 49932cad..034a17ef 100644 --- a/UPGRADE_C_API.rst +++ b/UPGRADE_C_API.rst @@ -1,67 +1,183 @@ +Upgrading PySlurm to a new Slurm Release +======================================== + Contents -------- * `Overview`_ -* `Directory Structure`_ * `Requirements`_ -* `Generating Code`_ -* `Compiling, Updating, Testing`_ +* `1. Create the branch`_ +* `2. Validate the generator`_ +* `3. Regenerate the bindings`_ +* `4. Audit extra.pxi`_ +* `5. Build and fix call sites`_ +* `6. Test`_ +* `7. Update version metadata`_ Overview -------- -This small guide shows how to update PySlurm to a new Major Slurm Release - specifically it shows -how to translate the C-API Headers into an appropriate file with cython definitions. - -Directory Structure -------------------- +PySlurm tracks Slurm major releases one-for-one: branch ``26.05.x`` targets +Slurm 26.05, ``25.11.x`` targets 25.11, and so on. Upgrading means pointing the +Cython definitions at the new C headers and repairing whatever the new release +broke. -All the Cython definitions for Slurm can be found in the directory :code:`pyslurm/slurm/` -Essentially, the two most important files are :code:`header.pxi` and :code:`extra.pxi`. -The first one contains all auto-generated definitions, the latter one contains definitions not found in the headers directly, but exported in `libslurm.so`. +The definitions live in :code:`pyslurm/slurm/`: -The Idea here is to simply have one branch for each Major release, e.g. `20.11`, `21.08`, `22.05` and so on. +* :code:`slurm.h.pxi`, :code:`slurmdb.h.pxi`, :code:`slurm_errno.h.pxi` are + **generated** from the Slurm headers by :code:`scripts/pyslurm_bindgen.py`. + Never edit them by hand. +* :code:`extra.pxi` is **hand-maintained**. It declares internal structs and + functions that are exported by ``libslurmfull.so`` but absent from the public + headers. This file is where upgrades go wrong quietly - see step 4. Requirements ------------ -- `autopxd2 `_ -- C-Preprocessor (*cpp*, *clang*) -- Slurm headers (*slurm.h*, *slurmdb.h*, *slurm_errno.h*) -- Cython compiler (latest stable) +* `autopxd2 `_ **2.5.0**. Pin it. The 3.x + series changed the API that :code:`pyslurm_bindgen.py` calls into. +* A C preprocessor (*cpp* or *clang*) +* Slurm headers for the target release (*slurm.h*, *slurmdb.h*, *slurm_errno.h*) +* Cython (latest stable) + +Work inside a container or VM that has the target Slurm release installed, so +that the headers, ``libslurmfull.so``, and a running ``slurmctld`` all match. + +1. Create the branch +-------------------- + +Name it after the Slurm major release: + +.. code-block:: bash -Generating Code ---------------- + git checkout -b 26.05.x -The script in :code:`scripts/pyslurm_bindgen.py` basically generates all of the needed definitions from the Header files. -Inside the script, `autopxd2` is used which helps to create Cython specific definitions for all structs and functions. -In addition, also all constants from the headers (`#define`) are made available with their appropriate data types. +2. Validate the generator +------------------------- -First of all, checkout a new branch in the Repository, and give it the name -of the major release to target, for example: +Before generating anything you intend to keep, run the generator against the +**previous** release's headers and diff the result against what is already +committed: .. code-block:: bash - git checkout -b 22.05 + scripts/pyslurm_bindgen.py -D /path/to/25.11/include/slurm -o /tmp/check + diff pyslurm/slurm/slurm.h.pxi /tmp/check/slurm.h.pxi + +Ignore the ``Generated on`` timestamp; everything else should be identical. If +it is not, your autopxd2 version or toolchain differs from the one that produced +the committed files, and every diff you see in step 3 will be noise. Fix that +first. -Then, simply generate the header definitions like in this example: +3. Regenerate the bindings +-------------------------- .. code-block:: bash - scripts/pyslurm_bindgen.py -D /directory/with/slurm/headers > pyslurm/slurm/header.pxi + scripts/pyslurm_bindgen.py -D /path/to/26.05/include/slurm + +The script writes one :code:`
.pxi` per header into +:code:`pyslurm/slurm/` (override with :code:`-o`, or use :code:`-s` to print to +stdout instead). It also translates ``#define`` macros into typed constants. -The script outputs everything to `stdout`. Simply redirect the output to the file: :code:`pyslurm/slurm/header.pxi`. -The headers should now be fully translated. +Now diff against the previous release's generated output. That diff *is* the +API change list: renamed functions, changed signatures, added and removed struct +members, new and deleted enum values. Read all of it. Anything that only changes +a struct member will compile cleanly and fail at runtime. +If the generator crashes, the new headers contain a construct autopxd2 cannot +parse. Prefer patching :code:`scripts/pyslurm_bindgen.py` over editing generated +output - see ``_patch_autopxd_enum_casts()`` there for an example, added when +26.05 started using ``SLURM_BIT()`` (which expands to a cast) for enum values. -Compiling, Updating, Testing ----------------------------- +Fix the fallout in :code:`pyslurm/pydefines/*.pxi`, which reference constants by +name and break loudly when one disappears. -Now with the generated headers, you can try and build pyslurm (e.g. by having a slurm installation in a virtual machine): +4. Audit extra.pxi +------------------ + +**Do not skip this step.** The structs in :code:`extra.pxi` are declared as +plain ``ctypedef struct``, not inside a ``cdef extern`` block, so Cython emits +its own C definition rather than deferring to a header. If a member is added +upstream and not mirrored here, every following member reads from the wrong +offset. Nothing warns you: the build succeeds and unit tests pass. + +Fetch the matching Slurm sources and compare each declaration field by field: .. code-block:: bash - python3 setup.py build + curl -O https://download.schedmd.com/slurm/slurm-26.05.2.tar.bz2 + tar xf slurm-26.05.2.tar.bz2 + +The structs and their homes: + +=========================== ========================================== +Declaration Slurm source +=========================== ========================================== +``job_resources`` ``src/common/job_resources.h`` +``slurm_msg_t`` ``src/common/slurm_protocol_defs.h`` +``forward_t`` ``src/common/slurm_protocol_defs.h`` +``forward_struct_t`` ``src/common/slurm_protocol_defs.h`` +``job_id_msg_t`` ``src/common/slurm_protocol_defs.h`` +``return_code_msg_t`` ``src/common/slurm_protocol_defs.h`` +``persist_conn_t`` ``src/common/persist_conn.h`` +``persist_msg_t`` ``src/common/persist_conn.h`` +``buf_t`` ``src/common/pack.h`` +``slurm_msg_type_t`` ``src/common/msg_type.h`` +``tres_types_t`` ``src/common/slurmdb_defs.h`` +=========================== ========================================== + +Check for three things: + +* **Added or removed members.** Order matters. A ``void *`` standing in for a + concrete pointer type is fine; a missing member is not. +* **Structs embedded by value.** ``forward_t`` sits inside ``slurm_msg_t``, so a + change to it silently resizes ``slurm_msg_t``. +* **Hardcoded enum values.** :code:`extra.pxi` pins a few ``slurm_msg_type_t`` + members to literal numbers. Slurm reuses retired RPC slots between releases, + so recompute them from the new ``msg_type.h``. + +Also check memory ownership. Slurm occasionally switches an API from returning +``strdup``-allocated memory to ``xmalloc``-allocated memory (26.05 did this to +``slurm_create_reservation``). Calling libc ``free()`` on an ``xmalloc`` pointer +corrupts the heap. Grepping the release's ``src/api/*.c`` for changes in the +``caller must free`` / ``caller must xfree`` comments catches these cheaply. + +5. Build and fix call sites +--------------------------- + +.. code-block:: bash + + scripts/build.sh -j8 -d + +Iterate until it compiles. Most breakage is mechanical: renamed functions, +changed argument types, deleted constants. A clean build means the code +*compiles* against the new API - it says nothing about steps 3 and 4 being +complete. + +6. Test +------- + +.. code-block:: bash + + scripts/run_tests.sh # unit + integration + +Integration tests need a running Slurm of the target version; they are what +catch the struct layout and memory ownership problems that the build cannot. +Run them before opening the PR. + +7. Update version metadata +-------------------------- + +Set :code:`pyslurm/version.py` to ``..0``. ``setup.py`` derives +the required Slurm version from it and refuses to build against a mismatched +``slurm_version.h``. + +Then update the remaining references: -This will likely give you a bunch of errors, since usually a few things have changed in between major releases. -Usually it is rather straightforward to adapt the code. Often only a few constants have been deleted/renamed. If no more errors are showing and it compiles, everything is done. +* :code:`pyslurm.spec` - ``Version``, ``BuildRequires: slurm-devel``, ``%changelog`` +* :code:`README.md` - requirements line and compatibility table +* :code:`CLAUDE.md` - required Slurm version +* :code:`CHANGELOG.md` - new section for the branch +* :code:`docker-compose.yml` - default image tag +* :code:`.github/workflows/pyslurm.yml` - image tags in the test matrix diff --git a/docker-compose.yml b/docker-compose.yml index 192c644e..0e6aca3f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: slurm: - image: ${SLURM_DOCKER_IMAGE:-giovtorres/slurm-docker:25.11.2} + image: ${SLURM_DOCKER_IMAGE:-giovtorres/slurm-docker:26.05.3} hostname: slurmctl container_name: slurmctl privileged: true diff --git a/pyslurm.spec b/pyslurm.spec index 2f547f08..5dbaea47 100644 --- a/pyslurm.spec +++ b/pyslurm.spec @@ -1,5 +1,5 @@ Name: python-pyslurm -Version: 25.11.2 +Version: 26.5.0 %define rel 1 Release: %{rel}%{?dist} Summary: Python interface to Slurm @@ -9,7 +9,7 @@ Source: pyslurm-%{version}.tar.gz BuildRequires: python3-devel BuildRequires: pyproject-rpm-macros -BuildRequires: slurm-devel >= 25.11.0 +BuildRequires: slurm-devel >= 26.05.0 %description pyslurm is a Python interface to Slurm @@ -39,7 +39,10 @@ pyslurm is a Python interface to Slurm %doc README.md %changelog -* Sat Apr 12 2026 Giovanni Torres - 25.11.2-1 +* Thu Aug 20 2026 Giovanni Torres - 26.5.0-1 +- Support for Slurm 26.05.x + +* Sat Apr 12 2026 Giovanni Torres - 25.11.2-1 - Fix Python 3.6 package metadata (UNKNOWN-0.0.0) by reading version from setup.py * Sun Mar 22 2026 Giovanni Torres - 25.11.0-1 diff --git a/pyslurm/core/job/job.pxd b/pyslurm/core/job/job.pxd index 191c7382..52ed1ea7 100644 --- a/pyslurm/core/job/job.pxd +++ b/pyslurm/core/job/job.pxd @@ -32,6 +32,7 @@ from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, int64_t from libc.stdlib cimport free from pyslurm.core.job.submission cimport JobSubmitDescription from pyslurm.core.job.step cimport JobSteps, JobStep +from pyslurm.utils.helpers cimport init_job_step_id from pyslurm.xcollections cimport MultiClusterMap from pyslurm cimport slurm from pyslurm.slurm cimport ( @@ -62,6 +63,7 @@ from pyslurm.slurm cimport ( slurm_update_job, slurm_notify_job, slurm_requeue, + slurm_step_id_t, xfree, try_xmalloc, ) @@ -287,6 +289,10 @@ cdef class Job: Name of the reservation this Job uses. resource_sharing (str): Mode controlling how a job shares resources with others. + exclusive (pyslurm.JobExclusive): + Exclusive resource allocation mode of the Job. + oversubscribe (pyslurm.JobOversubscribe): + Whether the Job is willing to oversubscribe resources. requires_contiguous_nodes (bool): Whether the Job has allocated a set of contiguous nodes. licenses (list): @@ -307,6 +313,12 @@ cdef class Job: The container this Job uses. container_id (str): The OCI ID of the Container this Job uses. + container_type (str): + Container plugin type the Job uses. + memory_update_delay (int): + Delay in minutes before memory is auto-reduced. + memory_update_margin (int): + Margin in percent applied when memory is auto-reduced. comment (str): An arbitrary comment set for the Job. standard_input (str): diff --git a/pyslurm/core/job/job.pyx b/pyslurm/core/job/job.pyx index 1b3eb989..92c6413e 100644 --- a/pyslurm/core/job/job.pyx +++ b/pyslurm/core/job/job.pyx @@ -41,6 +41,7 @@ from pyslurm.core.error import ( verify_rpc, slurm_errno, ) +from pyslurm.enums import JobExclusive, JobOversubscribe from pyslurm.utils.ctime import _raw_time from pyslurm.utils.helpers import ( uid_to_name, @@ -286,9 +287,10 @@ cdef class Job: cdef: job_info_msg_t *info = NULL Job wrap = None + slurm_step_id_t step_id = init_job_step_id(job_id) try: - verify_rpc(slurm_load_job(&info, job_id, slurm.SHOW_DETAIL)) + verify_rpc(slurm_load_job(&info, step_id, slurm.SHOW_DETAIL)) if info and info.record_count: wrap = Job.from_ptr(&info.job_array[0]) @@ -373,7 +375,9 @@ cdef class Job: >>> Job(9999).send_signal(9) """ - cdef uint16_t flags = 0 + cdef: + uint16_t flags = 0 + slurm_step_id_t step_id = init_job_step_id(self.id) if steps.casefold() == "all": flags |= slurm.KILL_FULL_JOB @@ -384,7 +388,7 @@ cdef class Job: flags |= slurm.KILL_HURRY sig = signal_to_num(signal) - slurm_kill_job(self.id, sig, flags) + slurm_kill_job(step_id, sig, flags) # Ignore errors when the Job is already done or when SIGKILL was # specified and the job id is already purged from slurmctlds memory. @@ -426,7 +430,8 @@ cdef class Job: # _slurm_rpc_suspend it should return ESLURM_INVALID_JOB_ID, but # returns -1 # https://github.com/SchedMD/slurm/blob/master/src/slurmctld/proc_req.c#L4693 - verify_rpc(slurm_suspend(self.id)) + cdef slurm_step_id_t step_id = init_job_step_id(self.id) + verify_rpc(slurm_suspend(step_id)) def unsuspend(self): """Unsuspend a currently suspended Job. @@ -441,7 +446,8 @@ cdef class Job: >>> pyslurm.Job(9999).unsuspend() """ # Same problem as described in suspend() - verify_rpc(slurm_resume(self.id)) + cdef slurm_step_id_t step_id = init_job_step_id(self.id) + verify_rpc(slurm_resume(step_id)) def modify(self, JobSubmitDescription changes): """Modify a Job. @@ -532,12 +538,14 @@ cdef class Job: >>> # Requeing a Job while putting it in a held state >>> pyslurm.Job(9999).requeue(hold=True) """ - cdef uint32_t flags = 0 + cdef: + uint32_t flags = 0 + slurm_step_id_t step_id = init_job_step_id(self.id) if hold: flags |= slurm.JOB_REQUEUE_HOLD - verify_rpc(slurm_requeue(self.id, flags)) + verify_rpc(slurm_requeue(step_id, flags)) def notify(self, msg): """Sends a message to the Jobs stdout. @@ -556,7 +564,8 @@ cdef class Job: >>> import pyslurm >>> pyslurm.Job(9999).notify("Hello Friends!") """ - verify_rpc(slurm_notify_job(self.id, msg)) + cdef slurm_step_id_t step_id = init_job_step_id(self.id) + verify_rpc(slurm_notify_job(step_id, msg)) def load_stats(self): """Load realtime statistics for a Job and its steps. @@ -984,6 +993,16 @@ cdef class Job: def resource_sharing(self): return cstr.to_unicode(slurm_job_share_string(self.ptr.shared)) + @property + def exclusive(self): + return JobExclusive.from_value(self.ptr.exclusive, + default=JobExclusive.NONE) + + @property + def oversubscribe(self): + return JobOversubscribe.from_value(self.ptr.oversubscribe, + default=JobOversubscribe.NO) + @property def requires_contiguous_nodes(self): return u16_parse_bool(self.ptr.contiguous) @@ -1024,6 +1043,18 @@ cdef class Job: def container_id(self): return cstr.to_unicode(self.ptr.container_id) + @property + def container_type(self): + return cstr.to_unicode(self.ptr.container_type) + + @property + def memory_update_delay(self): + return u16_parse(self.ptr.mem_update_delay) + + @property + def memory_update_margin(self): + return u16_parse(self.ptr.mem_update_margin) + @property def comment(self): return cstr.to_unicode(self.ptr.comment) diff --git a/pyslurm/core/job/sbatch_opts.pyx b/pyslurm/core/job/sbatch_opts.pyx index 37976407..c9e63c0e 100644 --- a/pyslurm/core/job/sbatch_opts.pyx +++ b/pyslurm/core/job/sbatch_opts.pyx @@ -80,6 +80,7 @@ SBATCH_OPTIONS = [ SbatchOpt(None, "comment","comment"), SbatchOpt("C", "constraint", "constraints"), SbatchOpt(None, "container", "container"), + SbatchOpt(None, "container-type", "container_type"), SbatchOpt(None, "contiguous", "requires_contiguous_nodes"), SbatchOpt("S", "core-spec", "cores_reserved_for_system"), SbatchOpt(None, "cores-per-socket", "cores_per_socket"), diff --git a/pyslurm/core/job/step.pxd b/pyslurm/core/job/step.pxd index 550ed495..e7be339e 100644 --- a/pyslurm/core/job/step.pxd +++ b/pyslurm/core/job/step.pxd @@ -126,6 +126,8 @@ cdef class JobStep: Path to the container OCI. container_id (str): The ID of the OCI Container. + container_type (str): + Container plugin type the Step uses. allocated_nodes (str): Nodes the Job is using. start_time (int): diff --git a/pyslurm/core/job/step.pyx b/pyslurm/core/job/step.pyx index fcf459fc..1d81c6bc 100644 --- a/pyslurm/core/job/step.pyx +++ b/pyslurm/core/job/step.pyx @@ -443,6 +443,10 @@ cdef class JobStep: def container_id(self): return cstr.to_unicode(self.ptr.container_id) + @property + def container_type(self): + return cstr.to_unicode(self.ptr.container_type) + def array_id(self): return u32_parse(self.ptr.array_job_id) diff --git a/pyslurm/core/job/submission.pxd b/pyslurm/core/job/submission.pxd index d96cbe07..30e283a2 100644 --- a/pyslurm/core/job/submission.pxd +++ b/pyslurm/core/job/submission.pxd @@ -331,6 +331,9 @@ cdef class JobSubmitDescription: container_id (str): Unique name for the Container This is the same as --container-id from sbatch. + container_type (str): + Container plugin type to use. + This is the same as --container-type from sbatch. cpus_per_task (int): The amount of cpus required for each task. @@ -624,6 +627,7 @@ cdef class JobSubmitDescription: time_limit_min container container_id + container_type cpus_per_task cpus_per_gpu sockets_per_node diff --git a/pyslurm/core/job/submission.pyx b/pyslurm/core/job/submission.pyx index 57a633eb..befe96fa 100644 --- a/pyslurm/core/job/submission.pyx +++ b/pyslurm/core/job/submission.pyx @@ -216,6 +216,7 @@ cdef class JobSubmitDescription: cstr.fmalloc(&ptr.qos, self.qos) cstr.fmalloc(&ptr.container, self.container) cstr.fmalloc(&ptr.container_id, self.container_id) + cstr.fmalloc(&ptr.container_type, self.container_type) cstr.fmalloc(&ptr.std_in, self.standard_in) cstr.fmalloc(&ptr.std_out, self.standard_output) cstr.fmalloc(&ptr.std_err, self.standard_error) diff --git a/pyslurm/core/node.pxd b/pyslurm/core/node.pxd index a33a94a3..5c2f9b63 100644 --- a/pyslurm/core/node.pxd +++ b/pyslurm/core/node.pxd @@ -201,6 +201,8 @@ cdef class Node: Time this node was last busy, as unix timestamp. reason_time (int): Time the reason was set for the node, as unix timestamp. + suspend_time (int): + Time in seconds the node must be idle before power save kicks in. allocated_tres (dict): Currently allocated Trackable Resources allocated_cpus (int): diff --git a/pyslurm/core/node.pyx b/pyslurm/core/node.pyx index 355faefb..0cc35d01 100644 --- a/pyslurm/core/node.pyx +++ b/pyslurm/core/node.pyx @@ -602,6 +602,10 @@ cdef class Node: def reason_time(self): return _raw_time(self.info.reason_time) + @property + def suspend_time(self): + return u32_parse(self.info.suspend_time) + # @property # def tres_configured(self): # """dict: TRES that are configured on the node.""" diff --git a/pyslurm/core/reservation.pxd b/pyslurm/core/reservation.pxd index 781c6e5a..a3762f48 100644 --- a/pyslurm/core/reservation.pxd +++ b/pyslurm/core/reservation.pxd @@ -24,7 +24,6 @@ from libc.string cimport memcpy, memset from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t -from libc.stdlib cimport free from pyslurm cimport slurm from pyslurm.slurm cimport ( reserve_info_t, diff --git a/pyslurm/core/reservation.pyx b/pyslurm/core/reservation.pyx index 26916932..470b8d81 100644 --- a/pyslurm/core/reservation.pyx +++ b/pyslurm/core/reservation.pyx @@ -259,7 +259,9 @@ cdef class Reservation: self.name = self._error_or_name() new_name = slurm_create_reservation(self.umsg) - free(new_name) + # Since 26.05, the returned name is allocated with xmalloc, so it must + # be released with xfree instead of free. + xfree(new_name) verify_rpc(slurm_errno()) return self diff --git a/pyslurm/core/slurmctld/config.pxd b/pyslurm/core/slurmctld/config.pxd index 25e3de27..a288c76a 100644 --- a/pyslurm/core/slurmctld/config.pxd +++ b/pyslurm/core/slurmctld/config.pxd @@ -29,7 +29,7 @@ from pyslurm cimport slurm from pyslurm.slurm cimport ( slurm_conf_t, slurm_load_ctl_conf, - slurm_free_ctl_conf, + slurm_free_conf, slurm_preempt_mode_string, slurm_accounting_enforce_string, slurm_sprint_cpu_bind_type, @@ -41,6 +41,7 @@ from pyslurm.slurm cimport ( from pyslurm.utils cimport cstr from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, int64_t from pyslurm.utils.uint cimport ( + u8_parse_bool, u16_parse, u32_parse, u64_parse, @@ -355,6 +356,8 @@ cdef class Config: health_check_program (str): Pathname of a script that is periodally executed as root user on all compute nodes. + health_check_timeout (int): + Time limit in seconds for the health check program. {slurm.conf#OPT_HealthCheckProgram} inactive_limit (int): @@ -446,6 +449,8 @@ cdef class Config: Options for the job launch plugin. {slurm.conf#OPT_LaunchParameters} + license_parameters (list[str]): + Options for the license management. licenses (dict[str, int]): Licenses that can be allocated to jobs. @@ -516,6 +521,12 @@ cdef class Config: Parameters for the MCS Plugin. {slurm.conf#OPT_MCSParameters} + metrics_auth (bool): + Whether authentication is required for the metrics endpoints. + metrics_auth_users (list[str]): + Users allowed to query the metrics plugins. + metrics_parameters (list[str]): + Options for the Metrics plugin. metrics_type (str): Name of the Metrics plugin used. @@ -824,6 +835,8 @@ cdef class Config: UID of the `slurmd_user_name` slurmd_user_name (str): Name of the User slurmd runs as. + slurmctld_http_auth_parameters (list[str]): + Options for `slurmctld` HTTP authentication. slurmctld_log_level (str): The level of detail to provide `slurmctld` daemon's logs. @@ -870,6 +883,8 @@ cdef class Config: option. {slurm.conf#OPT_SlurmctldParameters} + slurmd_http_auth_parameters (list[str]): + Options for `slurmd` HTTP authentication. slurmd_log_level (str): Level of detail `slurmd` is logging. diff --git a/pyslurm/core/slurmctld/config.pyx b/pyslurm/core/slurmctld/config.pyx index 0a9ed72e..b1d89317 100644 --- a/pyslurm/core/slurmctld/config.pyx +++ b/pyslurm/core/slurmctld/config.pyx @@ -193,7 +193,7 @@ cdef class Config: "Use slurmctld.Config.load() to get an instance.") def __dealloc__(self): - slurm_free_ctl_conf(self.ptr) + slurm_free_conf(self.ptr) self.ptr = NULL @staticmethod @@ -516,6 +516,10 @@ cdef class Config: def health_check_program(self): return cstr.to_unicode(self.ptr.health_check_program) + @property + def health_check_timeout(self): + return u16_parse(self.ptr.health_check_timeout) + @property def inactive_limit(self): return u16_parse(self.ptr.inactive_limit) @@ -596,6 +600,10 @@ cdef class Config: def launch_parameters(self): return cstr.to_list(self.ptr.launch_params) + @property + def license_parameters(self): + return cstr.to_list(self.ptr.license_params) + @property def licenses(self): return cstr.to_dict(self.ptr.licenses, delim1=",", @@ -679,6 +687,18 @@ cdef class Config: def mcs_parameters(self): return cstr.to_list(self.ptr.mcs_plugin_params) + @property + def metrics_auth(self): + return u8_parse_bool(self.ptr.metrics_auth) + + @property + def metrics_auth_users(self): + return cstr.to_list(self.ptr.metrics_auth_users) + + @property + def metrics_parameters(self): + return cstr.to_list(self.ptr.metrics_params) + @property def metrics_type(self): return cstr.to_unicode(self.ptr.metrics_type) @@ -980,6 +1000,10 @@ cdef class Config: def slurmd_user_name(self): return cstr.to_unicode(self.ptr.slurmd_user_name) + @property + def slurmctld_http_auth_parameters(self): + return cstr.to_list(self.ptr.slurmctld_http_auth_params) + @property def slurmctld_log_level(self): return _log_level_int_to_str(self.ptr.slurmctld_debug) @@ -1024,6 +1048,10 @@ cdef class Config: return cstr.to_dict(self.ptr.slurmctld_params, delim1=",", delim2="=", def_value=True) + @property + def slurmd_http_auth_parameters(self): + return cstr.to_list(self.ptr.slurmd_http_auth_params) + @property def slurmd_log_level(self): return _log_level_int_to_str(self.ptr.slurmd_debug) diff --git a/pyslurm/deprecated.pyx b/pyslurm/deprecated.pyx index cbf6e0a9..d8598e9f 100644 --- a/pyslurm/deprecated.pyx +++ b/pyslurm/deprecated.pyx @@ -55,7 +55,8 @@ cdef extern from "alps_cray.h" nogil: import builtins as __builtin__ from pyslurm cimport slurm -from pyslurm.slurm cimport xmalloc +from pyslurm.slurm cimport slurm_step_id_t, xmalloc +from pyslurm.utils.helpers cimport init_job_step_id, init_step_id import pyslurm.core.job include "pydefines/slurm_errno_defines.pxi" @@ -362,7 +363,7 @@ def get_controllers(): primary = stringOrNone(slurm_ctl_conf_ptr.control_machine[index], '') control_machs.append(primary) - slurm.slurm_free_ctl_conf(slurm_ctl_conf_ptr) + slurm.slurm_free_conf(slurm_ctl_conf_ptr) return control_machs @@ -447,8 +448,9 @@ cpdef long slurm_get_rem_time(uint32_t JobID=0) except? -1: Returns: int: Remaining time in seconds or -1 on error """ + cdef slurm_step_id_t step_id = init_job_step_id(JobID) cdef int apiError = 0 - cdef long errCode = slurm.slurm_get_rem_time(JobID) + cdef long errCode = slurm.slurm_get_rem_time(step_id) if errCode != 0: apiError = slurm_get_errno() @@ -466,9 +468,10 @@ cpdef time_t slurm_get_end_time(uint32_t JobID=0) except? -1: Returns: int: Remaining time in seconds or -1 on error """ + cdef slurm_step_id_t step_id = init_job_step_id(JobID) cdef time_t EndTime = -1 cdef int apiError = 0 - cdef int errCode = slurm.slurm_get_end_time(JobID, &EndTime) + cdef int errCode = slurm.slurm_get_end_time(step_id, &EndTime) if errCode != 0: apiError = slurm_get_errno() @@ -486,8 +489,9 @@ cpdef int slurm_job_node_ready(uint32_t JobID=0) except? -1: Returns: int: Node ready code. """ + cdef slurm_step_id_t step_id = init_job_step_id(JobID) cdef int apiError = 0 - cdef int errCode = slurm.slurm_job_node_ready(JobID) + cdef int errCode = slurm.slurm_job_node_ready(step_id) return errCode @@ -502,15 +506,15 @@ def slurm_pid2jobid(uint32_t JobPID=0): int: 0 for success or a slurm error code """ cdef: - uint32_t JobID = 0 + slurm_step_id_t step_id = init_step_id() int apiError = 0 - int errCode = slurm.slurm_pid2jobid(JobPID, &JobID) + int errCode = slurm.slurm_pid2jobid(JobPID, &step_id) if errCode != 0: apiError = slurm_get_errno() raise ValueError(stringOrNone(slurm.slurm_strerror(apiError), ''), apiError) - return errCode, JobID + return errCode, step_id.job_id # diff --git a/pyslurm/enums.pyx b/pyslurm/enums.pyx index acca2f6c..56c180cc 100644 --- a/pyslurm/enums.pyx +++ b/pyslurm/enums.pyx @@ -43,6 +43,36 @@ SchedulerType.MAIN.__doc__ = "Scheduled by the Main Scheduler" SchedulerType.SUBMIT.__doc__ = "Scheduled by the Backfill Scheduler" +class JobExclusive(SlurmEnum): + """Exclusive resource allocation mode of a Job.""" + NONE = "NONE", slurm.JOB_EXCLUSIVE_NONE + NODE = "NODE", slurm.JOB_EXCLUSIVE_NODE + USER = "USER", slurm.JOB_EXCLUSIVE_USER + MCS = "MCS", slurm.JOB_EXCLUSIVE_MCS + TOPO = "TOPO", slurm.JOB_EXCLUSIVE_TOPO + + +JobExclusive.NONE.__doc__ = "Nodes may be shared with other Jobs" +JobExclusive.NODE.__doc__ = "Nodes are allocated exclusively to this Job" +JobExclusive.USER.__doc__ = "Nodes are shared only with Jobs of the same User" +JobExclusive.MCS.__doc__ = "Nodes are shared only within the same MCS label" +JobExclusive.TOPO.__doc__ = "Topology segment is allocated exclusively" + + +class JobOversubscribe(SlurmEnum): + """Whether a Job is willing to oversubscribe resources.""" + NO = "NO", slurm.JOB_OVERSUBSCRIBE_NO + YES = "YES", slurm.JOB_OVERSUBSCRIBE_YES + OK = "OK", slurm.JOB_OVERSUBSCRIBE_OK + + +JobOversubscribe.NO.__doc__ = "Resources are not oversubscribed" +JobOversubscribe.YES.__doc__ = "Job wants to oversubscribe resources" +JobOversubscribe.OK.__doc__ = "Job accepts oversubscribed resources" + + __all__ = [ "SchedulerType", + "JobExclusive", + "JobOversubscribe", ] diff --git a/pyslurm/pydefines/slurm_errno_enums.pxi b/pyslurm/pydefines/slurm_errno_enums.pxi index 0c282f5e..2e373d0e 100644 --- a/pyslurm/pydefines/slurm_errno_enums.pxi +++ b/pyslurm/pydefines/slurm_errno_enums.pxi @@ -23,7 +23,7 @@ ESLURM_JOB_MISSING_REQUIRED_PARTITION_GROUP = slurm.ESLURM_JOB_MISSING_REQUIRED_ ESLURM_REQUESTED_NODES_NOT_IN_PARTITION = slurm.ESLURM_REQUESTED_NODES_NOT_IN_PARTITION ESLURM_TOO_MANY_REQUESTED_CPUS = slurm.ESLURM_TOO_MANY_REQUESTED_CPUS ESLURM_INVALID_NODE_COUNT = slurm.ESLURM_INVALID_NODE_COUNT -ESLURM_ERROR_ON_DESC_TO_RECORD_COPY = slurm.ESLURM_ERROR_ON_DESC_TO_RECORD_COPY +ESLURM_MAX_JOB_COUNT = slurm.ESLURM_MAX_JOB_COUNT ESLURM_JOB_MISSING_SIZE_SPECIFICATION = slurm.ESLURM_JOB_MISSING_SIZE_SPECIFICATION ESLURM_JOB_SCRIPT_MISSING = slurm.ESLURM_JOB_SCRIPT_MISSING ESLURM_USER_ID_MISSING = slurm.ESLURM_USER_ID_MISSING diff --git a/pyslurm/slurm/extra.pxi b/pyslurm/slurm/extra.pxi index e63ec3a4..15793999 100644 --- a/pyslurm/slurm/extra.pxi +++ b/pyslurm/slurm/extra.pxi @@ -5,7 +5,7 @@ # For example: to communicate with the slurmctld directly in order # to retrieve the actual batch-script as a string. # -# https://github.com/SchedMD/slurm/blob/slurm-24-11-0-1/src/common/persist_conn.h#L53 +# https://github.com/SchedMD/slurm/blob/slurm-26-05-2-1/src/common/persist_conn.h#L53 ctypedef enum persist_conn_type_t: PERSIST_TYPE_NONE = 0 PERSIST_TYPE_DBD @@ -14,9 +14,9 @@ ctypedef enum persist_conn_type_t: PERSIST_TYPE_HA_DBD PERSIST_TYPE_ACCT_UPDATE -# https://github.com/SchedMD/slurm/blob/slurm-24-11-0-1/src/common/persist_conn.h#L62 +# https://github.com/SchedMD/slurm/blob/slurm-26-05-2-1/src/common/persist_conn.h#L62 ctypedef struct persist_msg_t: - void *conn + void *pcon void *data uint16_t msg_type @@ -24,7 +24,7 @@ ctypedef int (*_persist_conn_t_callback_proc)(void *arg, persist_msg_t *msg, buf ctypedef void (*_persist_conn_t_callback_fini)(void *arg) -# https://github.com/SchedMD/slurm/blob/slurm-24-11-0-1/src/common/persist_conn.h#L68 +# https://github.com/SchedMD/slurm/blob/slurm-26-05-2-1/src/common/persist_conn.h#L68 ctypedef struct persist_conn_t: void *auth_cred uid_t auth_uid @@ -44,11 +44,13 @@ ctypedef struct persist_conn_t: time_t *shutdown pthread_t thread_id int timeout - void *tls_conn + void *conn + int last_fd + bool skip_conn_shutdown slurm_trigger_callbacks_t trigger_callbacks uint16_t version -# https://github.com/SchedMD/slurm/blob/slurm-24-11-0-1/src/common/pack.h#L68 +# https://github.com/SchedMD/slurm/blob/slurm-26-05-2-1/src/common/pack.h#L68 ctypedef struct buf_t: uint32_t magic char *head @@ -57,16 +59,16 @@ ctypedef struct buf_t: bool mmaped bool shadow -# https://github.com/SchedMD/slurm/blob/slurm-24-11-0-1/src/common/slurm_protocol_defs.h#L761 +# https://github.com/SchedMD/slurm/blob/slurm-26-05-2-1/src/common/slurm_protocol_defs.h#L761 ctypedef struct return_code_msg_t: uint32_t return_code -# https://github.com/SchedMD/slurm/blob/slurm-24-11-0-1/src/common/slurm_protocol_defs.h#L432 +# https://github.com/SchedMD/slurm/blob/slurm-26-05-2-1/src/common/slurm_protocol_defs.h#L432 ctypedef struct job_id_msg_t: slurm_step_id_t step_id uint16_t show_flags -# https://github.com/SchedMD/slurm/blob/slurm-24-05-3-1/src/common/msg_type.h#L45 +# https://github.com/SchedMD/slurm/blob/slurm-26-05-2-1/src/common/msg_type.h#L45 # Only partially defined - not everything needed at the moment. ctypedef enum slurm_msg_type_t: REQUEST_SHARE_INFO = 2022 @@ -74,7 +76,7 @@ ctypedef enum slurm_msg_type_t: RESPONSE_BATCH_SCRIPT = 2052 RESPONSE_SLURM_RC = 8001 -# https://github.com/SchedMD/slurm/blob/slurm-24-11-0-1/src/common/slurm_protocol_defs.h#L240 +# https://github.com/SchedMD/slurm/blob/slurm-26-05-2-1/src/common/slurm_protocol_defs.h#L240 ctypedef struct forward_t: slurm_node_alias_addrs_t alias_addrs uint16_t cnt @@ -84,12 +86,13 @@ ctypedef struct forward_t: uint16_t tree_width uint16_t tree_depth -# https://github.com/SchedMD/slurm/blob/slurm-24-11-0-1/src/common/slurm_protocol_defs.h#L269 +# https://github.com/SchedMD/slurm/blob/slurm-26-05-2-1/src/common/slurm_protocol_defs.h#L269 ctypedef struct forward_struct_t: slurm_node_alias_addrs_t *alias_addrs char *buf int buf_len uint16_t fwd_cnt + int thread_count pthread_mutex_t forward_mutex pthread_cond_t notify list_t *ret_list @@ -103,7 +106,7 @@ cdef extern from *: ctypedef struct conmgr_fd_t -# https://github.com/SchedMD/slurm/blob/slurm-25-11-4-1/src/common/slurm_protocol_defs.h +# https://github.com/SchedMD/slurm/blob/slurm-26-05-2-1/src/common/slurm_protocol_defs.h ctypedef struct slurm_msg_t: slurm_addr_t address void *auth_cred @@ -157,6 +160,7 @@ ctypedef struct job_resources: uint32_t next_step_node_inx uint32_t nhosts bitstr_t *node_bitmap + uint32_t *node_ranks uint32_t node_req char *nodes uint32_t ncpus diff --git a/pyslurm/slurm/slurm.h.pxi b/pyslurm/slurm/slurm.h.pxi index 0777dd4f..585ba92b 100644 --- a/pyslurm/slurm/slurm.h.pxi +++ b/pyslurm/slurm/slurm.h.pxi @@ -9,7 +9,7 @@ # * C-Macros are listed with their appropriate uint type # * Any definitions that cannot be translated are not included in this file # -# Generated on 2026-02-12T20:29:47.128029 +# Generated on 2026-08-20T15:33:57.231802 # # The Original Copyright notice from slurm.h has been included # below: @@ -217,6 +217,9 @@ cdef extern from "slurm/slurm.h": uint8_t PROP_PRIO_OFF uint8_t PROP_PRIO_ON uint8_t PROP_PRIO_NICER + uint8_t RETURN_TO_SERVICE_NONE + uint8_t RETURN_TO_SERVICE_NON_RESP + uint8_t RETURN_TO_SERVICE_ALL uint8_t PRIORITY_FLAGS_ACCRUE_ALWAYS uint8_t PRIORITY_FLAGS_MAX_TRES uint8_t PRIORITY_FLAGS_SIZE_RELATIVE @@ -275,6 +278,8 @@ cdef extern from "slurm/slurm.h": uint64_t SPREAD_SEGMENTS uint64_t CONSOLIDATE_SEGMENTS uint64_t EXPEDITED_REQUEUE + uint64_t NEED_MORE_FEATURES + uint64_t JOB_IMPLICIT_MAX_NODES uint8_t X11_FORWARD_ALL uint8_t X11_FORWARD_BATCH uint8_t X11_FORWARD_FIRST @@ -286,6 +291,14 @@ cdef extern from "slurm/slurm.h": uint8_t JOB_SHARED_USER uint8_t JOB_SHARED_MCS uint8_t JOB_SHARED_TOPO + uint8_t JOB_OVERSUBSCRIBE_NO + uint8_t JOB_OVERSUBSCRIBE_YES + uint8_t JOB_OVERSUBSCRIBE_OK + uint8_t JOB_EXCLUSIVE_NONE + uint8_t JOB_EXCLUSIVE_NODE + uint8_t JOB_EXCLUSIVE_USER + uint8_t JOB_EXCLUSIVE_MCS + uint8_t JOB_EXCLUSIVE_TOPO uint16_t CORE_SPEC_THREAD uint8_t JOB_DEF_CPU_PER_GPU uint8_t JOB_DEF_MEM_PER_GPU @@ -394,6 +407,7 @@ cdef extern from "slurm/slurm.h": uint64_t DEBUG_FLAG_BURST_BUF uint64_t DEBUG_FLAG_CPU_FREQ uint64_t DEBUG_FLAG_POWER + uint64_t DEBUG_FLAG_THREAD uint64_t DEBUG_FLAG_DB_ARCHIVE uint64_t DEBUG_FLAG_DB_TRES uint64_t DEBUG_FLAG_JOBCOMP @@ -426,6 +440,7 @@ cdef extern from "slurm/slurm.h": uint8_t HEALTH_CHECK_NODE_ANY uint16_t HEALTH_CHECK_CYCLE uint16_t HEALTH_CHECK_START_ONLY + uint16_t HEALTH_CHECK_REBOOT_ONLY uint8_t PROLOG_FLAG_ALLOC uint8_t PROLOG_FLAG_NOHOLD uint8_t PROLOG_FLAG_CONTAIN @@ -452,6 +467,7 @@ cdef extern from "slurm/slurm.h": uint16_t CONF_FLAG_CONTAIN_SPANK uint32_t CONF_FLAG_NO_STDIO uint32_t CONF_FLAG_DISABLE_HTTP + uint32_t CONF_FLAG_HC_REPORT_HEALTH uint8_t LOG_FMT_ISO8601_MS uint8_t LOG_FMT_ISO8601 uint8_t LOG_FMT_RFC5424_MS @@ -809,6 +825,10 @@ cdef extern from "slurm/slurm.h": AUTH_PLUGIN_JWT AUTH_PLUGIN_SLURM + cdef enum compress_plugin_type: + COMPRESS_PLUGIN_NONE + COMPRESS_PLUGIN_LZ4 + cdef enum hash_plugin_type: HASH_PLUGIN_DEFAULT HASH_PLUGIN_NONE @@ -993,6 +1013,7 @@ cdef extern from "slurm/slurm.h": SELECT_ONE_TASK_PER_CORE SELECT_PACK_NODES SELECT_LL_SHARED_GRES + SELECT_NO_DIST_TOPO_BLOCK SELECT_CORE_DEFAULT_DIST_BLOCK SELECT_LLN SELECT_MULTIPLE_SHARING_GRES_PJ @@ -1011,12 +1032,14 @@ cdef extern from "slurm/slurm.h": SSF_GRES_ALLOW_TASK_SHARING SSF_WAIT_FOR_CHILDREN SSF_KILL_ON_BAD_EXIT + SSF_ASYNC cdef enum topology_plugin_type: TOPOLOGY_PLUGIN_FLAT TOPOLOGY_PLUGIN_3DTORUS TOPOLOGY_PLUGIN_TREE TOPOLOGY_PLUGIN_BLOCK + TOPOLOGY_PLUGIN_RING void slurm_init(const char* conf) @@ -1125,6 +1148,7 @@ cdef extern from "slurm/slurm.h": uint16_t contiguous char* container char* container_id + char* container_type uint16_t core_spec char* cpu_bind uint16_t cpu_bind_type @@ -1223,6 +1247,8 @@ cdef extern from "slurm/slurm.h": uint16_t ntasks_per_tres uint16_t pn_min_cpus uint64_t pn_min_memory + uint16_t mem_update_delay + uint16_t mem_update_margin uint32_t pn_min_tmp_disk char* req_context uint32_t req_switch @@ -1266,6 +1292,7 @@ cdef extern from "slurm/slurm.h": char* comment char* container char* container_id + char* container_type uint16_t contiguous uint16_t core_spec uint16_t cores_per_socket @@ -1284,6 +1311,7 @@ cdef extern from "slurm/slurm.h": time_t end_time char* exc_nodes int32_t* exc_node_inx + uint16_t exclusive uint32_t exit_code char* extra char* failed_node @@ -1312,6 +1340,8 @@ cdef extern from "slurm/slurm.h": uint32_t max_nodes char* mcs_label char* mem_per_tres + uint16_t mem_update_delay + uint16_t mem_update_margin char* name char* network char* nodes @@ -1326,6 +1356,7 @@ cdef extern from "slurm/slurm.h": uint32_t num_nodes uint32_t num_tasks uint16_t oom_kill_step + uint16_t oversubscribe char* partition char* prefer uint64_t pn_min_memory @@ -1407,7 +1438,7 @@ cdef extern from "slurm/slurm.h": char* account char* cluster_name double direct_prio - uint32_t job_id + slurm_step_id_t step_id char* partition priority_factors_t* prio_factors char* qos @@ -1475,11 +1506,10 @@ cdef extern from "slurm/slurm.h": ctypedef struct slurm_step_layout_req_t: char* node_list uint16_t* cpus_per_node - uint32_t* cpu_count_reps + uint32_t* node_ranks uint32_t num_hosts uint32_t num_tasks uint16_t* cpus_per_task - uint32_t* cpus_task_reps uint32_t task_dist uint16_t plane_size @@ -1605,9 +1635,9 @@ cdef extern from "slurm/slurm.h": uint16_t ntasks_per_socket bool buffered_stdio bool labelio - char* remote_output_filename - char* remote_error_filename - char* remote_input_filename + char* output_filename + char* error_filename + char* input_filename slurm_step_io_fds_t local_fds bool multi_prog bool no_alloc @@ -1714,6 +1744,7 @@ cdef extern from "slurm/slurm.h": char* cluster char* container char* container_id + char* container_type uint32_t cpu_freq_min uint32_t cpu_freq_max uint32_t cpu_freq_gov @@ -1834,6 +1865,7 @@ cdef extern from "slurm/slurm.h": char* resv_name time_t slurmd_start_time uint16_t sockets + uint32_t suspend_time uint16_t threads uint32_t tmp_disk char* topology_str @@ -2104,7 +2136,7 @@ cdef extern from "slurm/slurm.h": char* accounting_storage_pass uint16_t accounting_storage_port char* accounting_storage_type - void* acct_gather_conf + list_t* acct_gather_conf char* acct_gather_energy_type char* acct_gather_profile_type char* acct_gather_interconnect_type @@ -2123,7 +2155,7 @@ cdef extern from "slurm/slurm.h": char* certgen_type char* certmgr_params char* certmgr_type - void* cgroup_conf + list_t* cgroup_conf char* cli_filter_params char* cli_filter_plugins uint16_t cluster_id @@ -2162,6 +2194,7 @@ cdef extern from "slurm/slurm.h": uint16_t health_check_interval uint16_t health_check_node_state char* health_check_program + uint16_t health_check_timeout char* http_parser_type uint32_t host_unreach_retry_count uint16_t inactive_limit @@ -2189,6 +2222,7 @@ cdef extern from "slurm/slurm.h": uint16_t kill_on_bad_exit uint16_t kill_wait char* launch_params + char* license_params char* licenses uint16_t log_fmt char* mail_domain @@ -2204,14 +2238,17 @@ cdef extern from "slurm/slurm.h": uint16_t max_tasks_per_node char* mcs_plugin char* mcs_plugin_params + uint8_t metrics_auth + char* metrics_auth_users + char* metrics_params char* metrics_type uint32_t min_job_age - void* mpi_conf + list_t* mpi_conf char* mpi_default char* mpi_params uint16_t msg_timeout uint32_t next_job_id - void* node_features_conf + list_t* node_features_conf char* node_features_plugins uint16_t over_time_limit char* plugindir @@ -2267,7 +2304,7 @@ cdef extern from "slurm/slurm.h": char* schedtype char* scron_params char* select_type - void* select_conf_key_pairs + list_t* select_conf_key_pairs uint16_t select_type_param char* site_factor_plugin char* site_factor_params @@ -2278,6 +2315,7 @@ cdef extern from "slurm/slurm.h": char* slurmd_user_name char* slurmctld_addr uint16_t slurmctld_debug + char* slurmctld_http_auth_params char* slurmctld_logfile char* slurmctld_pidfile uint32_t slurmctld_port @@ -2288,6 +2326,7 @@ cdef extern from "slurm/slurm.h": uint16_t slurmctld_timeout char* slurmctld_params uint16_t slurmd_debug + char* slurmd_http_auth_params char* slurmd_logfile char* slurmd_params char* slurmd_pidfile @@ -2367,8 +2406,8 @@ cdef extern from "slurm/slurm.h": char* node_hostname char* node_names uint32_t node_state + char* power_action_name char* reason - uint32_t reason_uid uint32_t resume_after char* topology_str uint32_t weight @@ -2545,17 +2584,17 @@ cdef extern from "slurm/slurm.h": ctypedef void (*_slurm_allocate_resources_blocking_pending_callback_ft)(slurm_step_id_t* step_id) - resource_allocation_response_msg_t* slurm_allocate_resources_blocking(const job_desc_msg_t* user_req, time_t timeout, _slurm_allocate_resources_blocking_pending_callback_ft pending_callback) + resource_allocation_response_msg_t* slurm_allocate_resources_blocking(const job_desc_msg_t* user_req, time_t timeout, _slurm_allocate_resources_blocking_pending_callback_ft pending_callback, int interrupt_fd) void slurm_free_resource_allocation_response_msg(resource_allocation_response_msg_t* msg) ctypedef void (*_slurm_allocate_het_job_blocking_pending_callback_ft)(slurm_step_id_t* step_id) - list_t* slurm_allocate_het_job_blocking(list_t* job_req_list, time_t timeout, _slurm_allocate_het_job_blocking_pending_callback_ft pending_callback) + list_t* slurm_allocate_het_job_blocking(list_t* job_req_list, time_t timeout, _slurm_allocate_het_job_blocking_pending_callback_ft pending_callback, int interrupt_fd) - int slurm_allocation_lookup(uint32_t job_id, resource_allocation_response_msg_t** resp) + int slurm_allocation_lookup(slurm_step_id_t step_id, resource_allocation_response_msg_t** resp) - int slurm_het_job_lookup(uint32_t jobid, list_t** resp) + int slurm_het_job_lookup(slurm_step_id_t step_id, list_t** resp) char* slurm_read_hostfile(const char* filename, int n) @@ -2569,7 +2608,7 @@ cdef extern from "slurm/slurm.h": void slurm_free_submit_response_response_msg(submit_response_msg_t* msg) - int slurm_job_batch_script(FILE* out, uint32_t jobid) + int slurm_job_batch_script(FILE* out, slurm_step_id_t step_id) int slurm_job_will_run(job_desc_msg_t* job_desc_msg) @@ -2632,7 +2671,7 @@ cdef extern from "slurm/slurm.h": kill_jobs_resp_job_t* job_responses uint32_t jobs_cnt - int slurm_kill_job(uint32_t job_id, uint16_t signal, uint16_t flags) + int slurm_kill_job(slurm_step_id_t step_id, uint16_t signal, uint16_t flags) int slurm_kill_job_step(slurm_step_id_t* step_id, uint16_t signal, uint16_t flags) @@ -2640,7 +2679,7 @@ cdef extern from "slurm/slurm.h": int slurm_kill_jobs(kill_jobs_msg_t* kill_msg, kill_jobs_resp_msg_t** kill_msg_resp) - int slurm_signal_job(uint32_t job_id, uint16_t signal) + int slurm_signal_job(slurm_step_id_t step_id, uint16_t signal) int slurm_signal_job_step(slurm_step_id_t* step_id, uint32_t signal) @@ -2666,15 +2705,15 @@ cdef extern from "slurm/slurm.h": long slurm_api_version() - int slurm_load_ctl_conf(time_t update_time, slurm_conf_t** slurm_ctl_conf_ptr) + int slurm_load_ctl_conf(time_t update_time, slurm_conf_t** slurm_conf_ptr) - void slurm_free_ctl_conf(slurm_conf_t* slurm_ctl_conf_ptr) + void slurm_free_conf(slurm_conf_t* slurm_conf_ptr) - void slurm_print_ctl_conf(FILE* out, slurm_conf_t* slurm_ctl_conf_ptr) + void slurm_print_ctl_conf(FILE* out, slurm_conf_t* slurm_conf_ptr) - void slurm_write_ctl_conf(slurm_conf_t* slurm_ctl_conf_ptr, node_info_msg_t* node_info_ptr, partition_info_msg_t* part_info_ptr) + void slurm_write_ctl_conf(slurm_conf_t* slurm_conf_ptr, node_info_msg_t* node_info_ptr, partition_info_msg_t* part_info_ptr) - void* slurm_ctl_conf_2_key_pairs(slurm_conf_t* slurm_ctl_conf_ptr) + void* slurm_ctl_conf_2_key_pairs(slurm_conf_t* slurm_conf_ptr) void slurm_print_key_pairs(FILE* out, void* key_pairs, char* title) @@ -2698,7 +2737,7 @@ cdef extern from "slurm/slurm.h": void slurm_free_priority_factors_response_msg(priority_factors_response_msg_t* factors_resp) - int slurm_get_end_time(uint32_t jobid, time_t* end_time_ptr) + int slurm_get_end_time(slurm_step_id_t step_id, time_t* end_time_ptr) void slurm_get_job_stderr(char* buf, int buf_size, job_info_t* job_ptr) @@ -2710,13 +2749,11 @@ cdef extern from "slurm/slurm.h": char* slurm_expand_job_stdio_fields(char* path, job_info_t* job) - long slurm_get_rem_time(uint32_t jobid) - - int slurm_job_node_ready(uint32_t job_id) + long slurm_get_rem_time(slurm_step_id_t step_id) - int slurm_load_job(job_info_msg_t** resp, uint32_t job_id, uint16_t show_flags) + int slurm_job_node_ready(slurm_step_id_t step_id) - int slurm_load_job_sluid(job_info_msg_t** resp, sluid_t sluid, uint16_t show_flags) + int slurm_load_job(job_info_msg_t** resp, slurm_step_id_t step_id, uint16_t show_flags) int slurm_load_job_prio(priority_factors_response_msg_t** factors_resp, uint16_t show_flags) @@ -2726,9 +2763,9 @@ cdef extern from "slurm/slurm.h": int slurm_load_job_state(int job_id_count, slurm_selected_step_t* job_ids, job_state_response_msg_t** jsr_pptr) - int slurm_notify_job(uint32_t job_id, char* message) + int slurm_notify_job(slurm_step_id_t step_id, char* message) - int slurm_pid2jobid(pid_t job_pid, uint32_t* job_id_ptr) + int slurm_pid2jobid(pid_t job_pid, slurm_step_id_t* step_id) int slurm_update_job(job_desc_msg_t* job_msg) @@ -2845,12 +2882,14 @@ cdef extern from "slurm/slurm.h": bool pinged long latency int offset + int rc ctypedef struct slurmdbd_ping_t: char* hostname bool pinged long latency int offset + int rc int slurm_ping(int dest) @@ -2880,17 +2919,17 @@ cdef extern from "slurm/slurm.h": int slurm_update_suspend_exc_states(char* states, update_mode_t mode) - int slurm_suspend(uint32_t job_id) + int slurm_suspend(slurm_step_id_t step_id) int slurm_suspend2(char* job_id, job_array_resp_msg_t** resp) - int slurm_resume(uint32_t job_id) + int slurm_resume(slurm_step_id_t step_id) int slurm_resume2(char* job_id, job_array_resp_msg_t** resp) void slurm_free_job_array_resp(job_array_resp_msg_t* resp) - int slurm_requeue(uint32_t job_id, uint32_t flags) + int slurm_requeue(slurm_step_id_t step_id, uint32_t flags) int slurm_requeue2(char* job_id, uint32_t flags, job_array_resp_msg_t** resp) @@ -2977,7 +3016,7 @@ cdef extern from "slurm/slurm.h": void slurm_print_burst_buffer_record(FILE* out, burst_buffer_info_t* burst_buffer_ptr, int one_liner, int verbose) - int slurm_network_callerid(network_callerid_msg_t req, uint32_t* job_id, char* node_name, int node_name_size) + int slurm_network_callerid(network_callerid_msg_t req, slurm_step_id_t* step_id, char* node_name, int node_name_size) int slurm_top_job(char* job_id_str) diff --git a/pyslurm/slurm/slurm_errno.h.pxi b/pyslurm/slurm/slurm_errno.h.pxi index 8fd4aa1a..dcb6be1c 100644 --- a/pyslurm/slurm/slurm_errno.h.pxi +++ b/pyslurm/slurm/slurm_errno.h.pxi @@ -9,7 +9,7 @@ # * C-Macros are listed with their appropriate uint type # * Any definitions that cannot be translated are not included in this file # -# Generated on 2026-02-12T20:29:47.013986 +# Generated on 2026-08-20T15:33:57.175291 # # The Original Copyright notice from slurm_errno.h has been included # below: @@ -75,6 +75,13 @@ cdef extern from "slurm/slurm_errno.h": SLURM_COMMUNICATIONS_INVALID_INCOMING_FD SLURM_COMMUNICATIONS_INVALID_OUTGOING_FD SLURM_COMMUNICATIONS_INVALID_FD + SLURM_BLOCKED_ON_READ + SLURM_BLOCKED_ON_WRITE + SLURM_COMMUNICATIONS_REJECTED + SLURM_COMMUNICATIONS_QUIESCE_TIMEOUT + SLURM_COMMUNICATIONS_CONNECT_TIMEOUT + SLURM_COMMUNICATIONS_WRITE_TIMEOUT + SLURM_COMMUNICATIONS_READ_TIMEOUT SLURMCTLD_COMMUNICATIONS_CONNECTION_ERROR SLURMCTLD_COMMUNICATIONS_SEND_ERROR SLURMCTLD_COMMUNICATIONS_RECEIVE_ERROR @@ -89,7 +96,7 @@ cdef extern from "slurm/slurm_errno.h": ESLURM_REQUESTED_NODES_NOT_IN_PARTITION ESLURM_TOO_MANY_REQUESTED_CPUS ESLURM_INVALID_NODE_COUNT - ESLURM_ERROR_ON_DESC_TO_RECORD_COPY + ESLURM_MAX_JOB_COUNT ESLURM_JOB_MISSING_SIZE_SPECIFICATION ESLURM_JOB_SCRIPT_MISSING ESLURM_USER_ID_MISSING @@ -146,6 +153,7 @@ cdef extern from "slurm/slurm_errno.h": ESLURM_PROLOG_RUNNING ESLURM_NO_STEPS ESLURM_MISSING_WORK_DIR + ESLURM_STEPS_DRAINED ESLURM_INVALID_QOS ESLURM_QOS_PREEMPTION_LOOP ESLURM_NODE_NOT_AVAIL @@ -260,10 +268,12 @@ cdef extern from "slurm/slurm_errno.h": ESLURM_PREEMPTION_REQUIRED ESLURM_INVALID_NODE_STATE_TRANSITION ESLURM_INVALID_JOB_STATE - ESLURM_BREAK_EVAL - ESLURM_RETRY_EVAL - ESLURM_RETRY_EVAL_HINT ESLURM_INVALID_SLUID + ESLURM_EXTERN_ONLY + ESLURM_INVALID_POWER_ACTION + ESLURM_STEP_QUEUED + ESLURM_INVALID_EXTERNAL_JOB + ESLURM_FILE_UNREADABLE ESPANK_ERROR ESPANK_BAD_ARG ESPANK_NOT_TASK @@ -327,6 +337,7 @@ cdef extern from "slurm/slurm_errno.h": ESLURM_NO_REMOVE_DEFAULT_QOS ESLURM_COORD_NO_INCREASE_JOB_LIMIT ESLURM_NO_RPC_STATS + ESLURM_INVALID_SHARED_POOL_ALLOWED ESLURM_FED_CLUSTER_MAX_CNT ESLURM_FED_CLUSTER_MULTIPLE_ASSIGNMENT ESLURM_INVALID_CLUSTER_FEATURE @@ -343,6 +354,16 @@ cdef extern from "slurm/slurm_errno.h": ESLURM_LUA_FUNC_FAILED_ENOMEM ESLURM_LUA_FUNC_FAILED_GARBAGE_COLLECTOR ESLURM_LUA_INVALID_CONVERSION_TYPE + ESLURM_TOPO_ERROR_START + ESLURM_TOPO_REQ_NODES_NOT_AVAIL + ESLURM_TOPO_REQ_NODES_NO_MATCH_TOPO + ESLURM_TOPO_NO_FIT + ESLURM_TOPO_SEGMENT_NO_FIT + ESLURM_TOPO_INSUFFICIENT_RESOURCES + ESLURM_TOPO_WEIGHT_NO_FIT + ESLURM_TOPO_MAX_NODE_LIMIT + ESLURM_TOPO_EMPTY_NODE_MAP + ESLURM_TOPO_ERROR_END ESLURM_MISSING_TIME_LIMIT ESLURM_PLUGIN_INVALID ESLURM_PLUGIN_INCOMPLETE @@ -362,6 +383,7 @@ cdef extern from "slurm/slurm_errno.h": ESLURM_REST_UNKNOWN_URL ESLURM_REST_UNKNOWN_URL_METHOD ESLURM_REST_AUTH_FAIL + ESLURM_REST_BAD_REQUEST ESLURM_DATA_PATH_NOT_FOUND ESLURM_DATA_PTR_NULL ESLURM_DATA_CONV_FAILED @@ -412,6 +434,7 @@ cdef extern from "slurm/slurm_errno.h": ESLURM_HTTP_POST_MISSING_CONTENT_LENGTH ESLURM_HTTP_UNEXPECTED_BODY ESLURM_HTTP_UNKNOWN_ACCEPT_MIME_TYPE + ESLURM_HTTP_MISSING_CR ESLURM_TLS_REQUIRED ctypedef struct slurm_errtab_t: diff --git a/pyslurm/slurm/slurmdb.h.pxi b/pyslurm/slurm/slurmdb.h.pxi index 0036afc5..d4360413 100644 --- a/pyslurm/slurm/slurmdb.h.pxi +++ b/pyslurm/slurm/slurmdb.h.pxi @@ -9,7 +9,7 @@ # * C-Macros are listed with their appropriate uint type # * Any definitions that cannot be translated are not included in this file # -# Generated on 2026-02-12T20:29:47.283339 +# Generated on 2026-08-20T15:33:57.315057 # # The Original Copyright notice from slurmdb.h has been included # below: @@ -55,6 +55,7 @@ cdef extern from "slurm/slurmdb.h": uint32_t SLURMDB_RES_FLAG_ADD uint32_t SLURMDB_RES_FLAG_REMOVE uint8_t SLURMDB_RES_FLAG_ABSOLUTE + uint8_t SLURMDB_RES_FLAG_SHARED_POOL uint32_t FEDERATION_FLAG_BASE uint32_t FEDERATION_FLAG_NOTSET uint32_t FEDERATION_FLAG_ADD @@ -103,6 +104,10 @@ cdef extern from "slurm/slurmdb.h": uint8_t DB_CONN_FLAG_CLUSTER_DEL uint8_t DB_CONN_FLAG_ROLLBACK uint8_t DB_CONN_FLAG_FEDUPDATE + uint8_t DEFAULT_SLURMDBD_KEEPALIVE_INTERVAL + uint8_t DEFAULT_SLURMDBD_KEEPALIVE_PROBES + uint8_t DEFAULT_SLURMDBD_KEEPALIVE_TIME + uint16_t DEFAULT_SLURMDBD_MAX_PURGE_LIMIT uint8_t COORD_SET_INDIRECT uint8_t COORD_SET_DIRECT uint8_t COORD_SET_BY_ACCT @@ -220,6 +225,49 @@ cdef extern from "slurm/slurmdb.h": ASSOC_FLAG_USER_COORD ASSOC_FLAG_BLOCK_ADD + cdef enum dbd_conf_flag: + DBD_CONF_FLAG_ALLOW_NO_DEF_ACCT + DBD_CONF_FLAG_ALL_RES_ABS + DBD_CONF_FLAG_DISABLE_COORD_DBD + DBD_CONF_FLAG_GET_DBVER + DBD_CONF_FLAG_DISABLE_ARCHIVE_COMMANDS + DBD_CONF_FLAG_DISABLE_ROLLUPS + + ctypedef struct slurmdbd_conf_t: + char* archive_dir + char* archive_script + uint16_t commit_delay + char* dbd_addr + char* dbd_backup + char* dbd_host + uint16_t dbd_port + uint16_t debug_level + char* default_qos + uint32_t flags + char* log_file + uint32_t max_purge_limit + uint32_t max_time_range + char* parameters + uint16_t persist_conn_rc_flags + char* pid_file + uint32_t purge_event + uint32_t purge_job + uint32_t purge_resv + uint32_t purge_step + uint32_t purge_suspend + uint32_t purge_txn + uint32_t purge_usage + uint32_t purge_jobscript + uint32_t purge_jobenv + char* storage_loc + char* storage_pass_script + char* storage_user + uint16_t syslog_debug + uint16_t track_wckey + uint16_t track_ctld + + void slurmdbd_free_conf(slurmdbd_conf_t* conf) + ctypedef struct slurmdb_tres_rec_t: uint64_t alloc_secs uint32_t rec_count @@ -334,6 +382,8 @@ cdef extern from "slurm/slurmdb.h": uint32_t purge_suspend uint32_t purge_txn uint32_t purge_usage + uint32_t purge_jobscript + uint32_t purge_jobenv ctypedef struct slurmdb_archive_rec_t: char* archive_file @@ -398,6 +448,7 @@ cdef extern from "slurm/slurmdb.h": uint32_t shares_raw uint32_t uid slurmdb_assoc_usage_t* usage + slurmdb_assoc_usage_t* usage_het char* user slurmdb_user_rec_t* user_rec @@ -576,6 +627,7 @@ cdef extern from "slurm/slurmdb.h": time_t eligible time_t end char* env + char* exclusive uint32_t exitcode char* extra char* failed_node @@ -590,6 +642,7 @@ cdef extern from "slurm/slurmdb.h": char* licenses char* mcs_label char* nodes + char* oversubscribe char* partition uint32_t priority uint32_t qosid @@ -604,6 +657,7 @@ cdef extern from "slurm/slurmdb.h": char* script uint16_t segment_size uint32_t show_full + uint64_t sluid time_t start uint32_t state uint32_t state_reason_prev @@ -697,6 +751,7 @@ cdef extern from "slurm/slurmdb.h": uint32_t priority uint64_t* relative_tres_cnt slurmdb_qos_usage_t* usage + slurmdb_qos_usage_t* usage_het double usage_factor double usage_thres @@ -960,11 +1015,11 @@ cdef extern from "slurm/slurmdb.h": ctypedef struct slurmdb_rollup_stats_t: char* cluster_name - uint16_t count[4] - time_t timestamp[4] - uint64_t time_last[4] - uint64_t time_max[4] - uint64_t time_total[4] + uint16_t count[3] + time_t timestamp[3] + uint64_t time_last[3] + uint64_t time_max[3] + uint64_t time_total[3] ctypedef struct slurmdb_rpc_obj_t: uint32_t cnt @@ -1065,7 +1120,9 @@ cdef extern from "slurm/slurmdb.h": int slurmdb_get_stats(void* db_conn, slurmdb_stats_rec_t** stats_pptr) - list_t* slurmdb_config_get(void* db_conn) + list_t* slurmdb_config_get_keypairs(const slurmdbd_conf_t* slurmdbd_conf) + + int slurmdb_config_get(void* db_conn, slurmdbd_conf_t** slurmdbd_conf_ptr) list_t* slurmdb_events_get(void* db_conn, slurmdb_event_cond_t* event_cond) diff --git a/pyslurm/utils/enums.pyx b/pyslurm/utils/enums.pyx index 55b86662..83f9c81d 100644 --- a/pyslurm/utils/enums.pyx +++ b/pyslurm/utils/enums.pyx @@ -83,6 +83,16 @@ class SlurmEnum(str, Enum, metaclass=DocstringSupport): return item return out + @classmethod + def from_value(cls, value, default): + # Unlike from_flag, this matches on the exact value. Use it for Slurm + # members that hold one of a set of consecutive constants rather than + # a bitmask. + for item in cls: + if item._flag == value: + return item + return cls(default) + class SlurmFlag(Flag, metaclass=DocstringSupport): diff --git a/pyslurm/utils/helpers.pxd b/pyslurm/utils/helpers.pxd index 60b817e8..3f5455ac 100644 --- a/pyslurm/utils/helpers.pxd +++ b/pyslurm/utils/helpers.pxd @@ -33,3 +33,4 @@ cpdef uid_to_name(uint32_t uid, err_on_invalid=*, dict lookup=*) cpdef gid_to_name(uint32_t gid, err_on_invalid=*, dict lookup=*) cpdef gres_from_tres_dict(dict tres_dict) cdef slurm_step_id_t init_step_id() +cdef slurm_step_id_t init_job_step_id(uint32_t job_id) diff --git a/pyslurm/utils/helpers.pyx b/pyslurm/utils/helpers.pyx index f80de98b..62adaa8d 100644 --- a/pyslurm/utils/helpers.pyx +++ b/pyslurm/utils/helpers.pyx @@ -439,3 +439,11 @@ cdef slurm_step_id_t init_step_id(): _s.step_het_comp = slurm.NO_VAL _s.step_id = slurm.NO_VAL return _s + + +cdef slurm_step_id_t init_job_step_id(uint32_t job_id): + # Since 26.05, RPCs that used to take a plain job_id now take a + # slurm_step_id_t. Leaving step_id as NO_VAL addresses the Job itself. + cdef slurm_step_id_t _s = init_step_id() + _s.job_id = job_id + return _s diff --git a/pyslurm/version.py b/pyslurm/version.py index 3a5fcf8f..fdcf63d3 100644 --- a/pyslurm/version.py +++ b/pyslurm/version.py @@ -5,4 +5,4 @@ # The last Number "Z" is the current Pyslurm patch version, which should be # incremented each time a new release is made (except when migrating to a new # Slurm Major release, then set it back to 0) -__version__ = "25.11.2" +__version__ = "26.5.0" diff --git a/scripts/pyslurm_bindgen.py b/scripts/pyslurm_bindgen.py index 2372bfc4..4a417172 100755 --- a/scripts/pyslurm_bindgen.py +++ b/scripts/pyslurm_bindgen.py @@ -21,11 +21,46 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import autopxd +import autopxd.writer import click from datetime import datetime import os import pathlib from collections import OrderedDict +from pycparser import c_ast + + +def _strip_casts(node): + """Recursively remove Cast nodes from an expression.""" + while isinstance(node, c_ast.Cast): + node = node.expr + + for attr in ("left", "right", "expr"): + child = getattr(node, attr, None) + if isinstance(child, c_ast.Node): + setattr(node, attr, _strip_casts(child)) + + return node + + +def _patch_autopxd_enum_casts(): + """Teach autopxd2 to handle cast expressions in enum values. + + Slurm defines SLURM_BIT(offset) as ((uint64_t)1 << offset) and uses it + for enum values, which the preprocessor expands into a Cast node that + autopxd2 refuses to parse. The values are only used internally by + autopxd2 to resolve array dimensions and never end up in the generated + output, so dropping the cast is safe here. + """ + original = autopxd.writer.parse_enum_value + + def parse_enum_value(node, constants): + return original(_strip_casts(node), constants) + + autopxd.writer.parse_enum_value = parse_enum_value + + +_patch_autopxd_enum_casts() UINT8_RANGE = range((2**8)) UINT16_RANGE = range((2**16)) diff --git a/tests/integration/test_job.py b/tests/integration/test_job.py index 00a41173..d9319a03 100644 --- a/tests/integration/test_job.py +++ b/tests/integration/test_job.py @@ -27,6 +27,8 @@ from util import create_simple_job_desc from pyslurm import ( Job, + JobExclusive, + JobOversubscribe, Jobs, JobSubmitDescription, RPCError, @@ -232,3 +234,68 @@ def test_to_json(submit_job): assert dict_data assert json_data assert len(dict_data) >= 3 + + +def test_new_26_05_members(submit_job): + job = Job.load(submit_job().id) + + # Not configured in the test cluster, but must decode without error + # rather than returning a raw integer. + assert isinstance(job.exclusive, JobExclusive) + assert isinstance(job.oversubscribe, JobOversubscribe) + assert job.memory_update_delay is None + assert job.memory_update_margin is None + + job_dict = job.to_dict() + for key in ("exclusive", "oversubscribe", "container_type", + "memory_update_delay", "memory_update_margin"): + assert key in job_dict + + # Enums must survive JSON serialization as their string name. + data = json.loads(Jobs.load().to_json())[str(job.id)] + assert data["exclusive"] == str(job.exclusive) + assert data["oversubscribe"] == str(job.oversubscribe) + + +def test_exclusive_job(submit_job): + job = Job.load(submit_job(resource_sharing="no").id) + assert job.exclusive == JobExclusive.NODE + + +def test_step_container_type(submit_job): + job = submit_job() + util.wait_for_job_running(job.id) + job = Job.load(job.id) + + for step in job.steps.values(): + assert step.container_type is None + assert "container_type" in step.to_dict() + + +def test_container_type_roundtrip(submit_job): + # Asserting only "is None" cannot tell container_type apart from any other + # unset string member, so round-trip a real value through slurmctld. + job = Job.load(submit_job(container_type="docker").id) + + assert job.container_type == "docker" + assert job.container_id is None + assert job.container is None + + +def test_get_resource_layout_per_node(submit_job): + # This walks job_resources, a struct pyslurm declares by hand in + # extra.pxi. A missing or reordered member there shifts every following + # field, so assert the decoded values are actually coherent. + job = submit_job() + util.wait_for_job_running(job.id) + job = Job.load(job.id) + + layout = job.get_resource_layout_per_node() + assert layout + assert set(layout) <= set(job.allocated_nodes.split(",")) + + for node_name, info in layout.items(): + assert node_name + assert info["cpu_ids"] + assert isinstance(info["memory"], int) + assert info["memory"] > 0 diff --git a/tests/integration/test_node.py b/tests/integration/test_node.py index 07eaba10..fcac2805 100644 --- a/tests/integration/test_node.py +++ b/tests/integration/test_node.py @@ -64,3 +64,15 @@ def test_to_json(): assert dict_data assert len(dict_data) >= 1 assert json_data + + +def test_suspend_time(): + name, _ = Nodes.load().popitem() + node = Node.load(name) + + # Power save is off in the test cluster, so Slurm reports INFINITE. + # Assert the shape too: a plain ">0" check would also pass if this + # accidentally read one of the neighbouring time_t members. + assert node.suspend_time in (None, "UNLIMITED") + assert node.suspend_time != node.boot_time + assert "suspend_time" in node.to_dict() diff --git a/tests/integration/test_slurmctld.py b/tests/integration/test_slurmctld.py index c494c089..d0b78f8c 100644 --- a/tests/integration/test_slurmctld.py +++ b/tests/integration/test_slurmctld.py @@ -117,3 +117,16 @@ def test_statistics(): assert new_stats.to_dict() # Check that resetting it was actually successful. assert data_since < new_stats.data_since + + +def test_new_26_05_config_members(): + config = slurmctld.Config.load() + + assert config.health_check_timeout is None or \ + config.health_check_timeout > 0 + assert isinstance(config.metrics_auth, bool) + assert isinstance(config.license_parameters, list) + assert isinstance(config.metrics_auth_users, list) + assert isinstance(config.metrics_parameters, list) + assert isinstance(config.slurmctld_http_auth_parameters, list) + assert isinstance(config.slurmd_http_auth_parameters, list) diff --git a/tests/unit/test_common.py b/tests/unit/test_common.py index 8e9bdf7a..f477d1d7 100644 --- a/tests/unit/test_common.py +++ b/tests/unit/test_common.py @@ -451,3 +451,14 @@ def test_nodelist_to_range_str(self): nodelist_str = ",".join(nodelist) assert "node[001,007-009]" == nodelist_to_range_str(nodelist) assert "node[001,007-009]" == nodelist_to_range_str(nodelist_str) + + +def test_renamed_errno_constant(): + # ESLURM_ERROR_ON_DESC_TO_RECORD_COPY was replaced by ESLURM_MAX_JOB_COUNT + # in Slurm 26.05. Make sure it resolves to its own distinct value and not + # to a neighbouring errno. + assert not hasattr(pyslurm, "ESLURM_ERROR_ON_DESC_TO_RECORD_COPY") + assert pyslurm.ESLURM_MAX_JOB_COUNT != pyslurm.ESLURM_INVALID_NODE_COUNT + assert pyslurm.ESLURM_MAX_JOB_COUNT != pyslurm.ESLURM_TOO_MANY_REQUESTED_CPUS + assert (pyslurm.ESLURM_MAX_JOB_COUNT + == pyslurm.ESLURM_INVALID_NODE_COUNT + 1) diff --git a/tests/unit/test_job.py b/tests/unit/test_job.py index 91b901c6..46e615f2 100644 --- a/tests/unit/test_job.py +++ b/tests/unit/test_job.py @@ -20,7 +20,7 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """test_job.py - Unit test basic job functionalities.""" -from pyslurm import Job +from pyslurm import Job, JobExclusive, JobOversubscribe from pyslurm.core.job.util import ( acctg_profile_int_to_list, dependency_str_to_dict, @@ -69,3 +69,28 @@ def test_acctg_profile_int_to_list(): def test_cpu_freq_int_to_str(): expected = None assert cpu_freq_int_to_str(0) == expected + + +def test_job_exclusive_from_value(): + assert JobExclusive.from_value(0, default=JobExclusive.NONE) == "NONE" + assert JobExclusive.from_value(1, default=JobExclusive.NONE) == "NODE" + assert JobExclusive.from_value(2, default=JobExclusive.NONE) == "USER" + assert JobExclusive.from_value(3, default=JobExclusive.NONE) == "MCS" + assert JobExclusive.from_value(4, default=JobExclusive.NONE) == "TOPO" + + +def test_job_exclusive_unknown_value_falls_back_to_default(): + # A value from a newer Slurm must not raise, and must not be misdecoded + # as some unrelated member. + assert JobExclusive.from_value(99, default=JobExclusive.NONE) == "NONE" + + +def test_job_oversubscribe_from_value(): + assert JobOversubscribe.from_value(0, default=JobOversubscribe.NO) == "NO" + assert JobOversubscribe.from_value(1, default=JobOversubscribe.NO) == "YES" + assert JobOversubscribe.from_value(2, default=JobOversubscribe.NO) == "OK" + + +def test_job_exclusive_is_str_comparable(): + assert JobExclusive.MCS == "MCS" + assert str(JobExclusive.MCS) == "MCS"