Skip to content

Commit dc3e8c4

Browse files
committed
wip documentation
1 parent b3646dc commit dc3e8c4

File tree

2 files changed

+120
-34
lines changed

2 files changed

+120
-34
lines changed

pyslurm/core/reservation.pxd

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,76 @@ cdef extern void slurm_free_reserve_info_members(reserve_info_t *resv)
5353

5454

5555
cdef class Reservations(MultiClusterMap):
56+
"""A [`Multi Cluster`][pyslurm.xcollections.MultiClusterMap] collection of [pyslurm.Reservation][] objects.
57+
58+
Args:
59+
reservations (Union[list[str], dict[str, pyslurm.Reservation], str], optional=None):
60+
Reservations to initialize this collection with.
61+
"""
5662

5763
cdef:
5864
reserve_info_msg_t *info
5965
reserve_info_t tmp_info
6066

6167

6268
cdef class Reservation:
69+
"""A Slurm Reservation.
70+
71+
Args:
72+
name (str, optional=None):
73+
Name of a Reservation.
74+
75+
!!! note
76+
77+
All Attributes of a Reservation, except for `name` and `cpus_by_node`,
78+
are eligible to be updated. Although the `name` attribute can be
79+
changed on the instance, the change will not be taken into account by
80+
`slurmctld`
6381
82+
Attributes:
83+
accounts (list[str]):
84+
List of account names that have access to the Reservation.
85+
burst_buffer (str):
86+
Burst Buffer specification.
87+
comment (str):
88+
Arbitrary comment for the Reservation.
89+
cpus (int):
90+
Amount of CPUs used by the Reservation
91+
cpus_by_node (dict[str, int]):
92+
A Mapping where each key is the node-name, and the values are a
93+
string of CPU-IDs reserved on the specific nodes.
94+
end_time (int):
95+
Unix Timestamp when the Reservation ends.
96+
features (list[str]):
97+
List of features required by the Reservation.
98+
groups (list[str]):
99+
List of Groups that can access the Reservation.
100+
licenses (list[str]):
101+
List of licenses to be reserved.
102+
max_start_delay (int):
103+
TODO
104+
name (str):
105+
Name of the Reservation.
106+
node_count (int):
107+
Count of Nodes required.
108+
nodes (str):
109+
Nodes to be reserved.
110+
partition (str):
111+
Name of the partition to be used.
112+
start_time (int):
113+
When the Reservation starts. This is a Unix timestamp.
114+
duration (int):
115+
How long, in minutes, the reservation runs for.
116+
is_active (bool):
117+
Whether the reservation is currently active or not.
118+
tres (dict[str, int])
119+
TRES for the Reservation.
120+
users (list[str]):
121+
List of user names permitted to use the Reservation.
122+
"""
64123
cdef:
65124
reserve_info_t *info
66125
resv_desc_msg_t *umsg
67-
dict passwd
68-
dict groups
69126

70127
cdef readonly cluster
71128

pyslurm/core/reservation.pyx

Lines changed: 61 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ cdef class Reservations(MultiClusterMap):
5353

5454
def __dealloc__(self):
5555
slurm_free_reservation_info_msg(self.info)
56+
self.info = NULL
5657

5758
def __cinit__(self):
5859
self.info = NULL
@@ -81,15 +82,19 @@ cdef class Reservations(MultiClusterMap):
8182

8283
verify_rpc(slurm_load_reservations(0, &reservations.info))
8384

84-
# zero-out a dummy reserve_info_t
85+
# prepare a dummy reserve_info_t struct.
8586
memset(&reservations.tmp_info, 0, sizeof(reserve_info_t))
8687

8788
# Put each pointer into its own instance.
8889
for cnt in range(reservations.info.record_count):
8990
reservation = Reservation.from_ptr(&reservations.info.reservation_array[cnt])
9091

91-
# Prevent double free if xmalloc fails mid-loop and a MemoryError
92-
# is raised by replacing it with a zeroed-out reserve_info_t.
92+
# If we already parsed at least one Reservation, and if for some
93+
# reason a MemoryError is raised after parsing subsequent
94+
# reservations, invalid behaviour will be shown by Valgrind, since
95+
# the Memory for the already parsed Reservation will be freed
96+
# twice. So for all sucessfully parsed Reservations, replace it
97+
# with a dummy struct that will be skipped in case of error.
9398
reservations.info.reservation_array[cnt] = reservations.tmp_info
9499

95100
cluster = reservation.cluster
@@ -112,8 +117,6 @@ cdef class Reservation:
112117
def __init__(self, name=None, **kwargs):
113118
self._alloc_impl()
114119
self.name = name
115-
self.start_time = datetime.now()
116-
self.duration = "365-00:00:00"
117120
self.cluster = LOCAL_CLUSTER
118121
for k, v in kwargs.items():
119122
setattr(self, k, v)
@@ -133,14 +136,17 @@ cdef class Reservation:
133136
self.umsg = <resv_desc_msg_t*>try_xmalloc(sizeof(resv_desc_msg_t))
134137
if not self.umsg:
135138
raise MemoryError("xmalloc failed for resv_desc_msg_t")
136-
print("we here")
137139
slurm_init_resv_desc_msg(self.umsg)
138140

139-
def _dealloc_impl(self):
141+
def _dealloc_umsg(self):
140142
slurm_free_resv_desc_msg(self.umsg)
141143
self.umsg = NULL
144+
145+
def _dealloc_impl(self):
146+
self._dealloc_umsg()
142147
slurm_free_reserve_info_members(self.info)
143148
xfree(self.info)
149+
self.info = NULL
144150

145151
def __dealloc__(self):
146152
self._dealloc_impl()
@@ -161,8 +167,6 @@ cdef class Reservation:
161167
cdef Reservation from_ptr(reserve_info_t *in_ptr):
162168
cdef Reservation wrap = Reservation.__new__(Reservation)
163169
wrap._alloc_info()
164-
wrap.passwd = {}
165-
wrap.groups = {}
166170
wrap.cluster = LOCAL_CLUSTER
167171
memcpy(wrap.info, in_ptr, sizeof(reserve_info_t))
168172
return wrap
@@ -175,15 +179,16 @@ cdef class Reservation:
175179
return self.name
176180

177181
def to_dict(self):
178-
"""Node information formatted as a dictionary.
182+
"""Reservation information formatted as a dictionary.
179183
180184
Returns:
181-
(dict): Node information as dict
185+
(dict): Reservation information as dict
182186
183187
Examples:
184188
>>> import pyslurm
185-
>>> mynode = pyslurm.Node.load("mynode")
186-
>>> mynode_dict = mynode.to_dict()
189+
>>> resv = pyslurm.Reservation.load("maintenance")
190+
>>> resv_dict = resv.to_dict()
191+
>>> print(resv_dict)
187192
"""
188193
return instance_to_dict(self)
189194

@@ -215,6 +220,10 @@ cdef class Reservation:
215220
def create(self):
216221
"""Create a Reservation.
217222
223+
If you did not specify atleast a `start_time` and `duration` or
224+
`end_time`, then by default the Reservation will start effective
225+
immediately, with a duration of one year.
226+
218227
Returns:
219228
(pyslurm.Reservation): This function returns the current
220229
Reservation instance object itself.
@@ -224,24 +233,29 @@ cdef class Reservation:
224233
225234
Examples:
226235
>>> import pyslurm
227-
>>> reservation = pyslurm.Reservation("debug").create()
236+
>>> resv = pyslurm.Reservation("debug")
228237
"""
229238
cdef char* new_name = NULL
230239

240+
if not self.start_time or not (self.duration and self.end_time):
241+
raise RPCError(msg="You must atleast specify a start_time, "
242+
" combined with an end_time or a duration.")
243+
231244
self.name = self._error_or_name()
232245
new_name = slurm_create_reservation(self.umsg)
233246
free(new_name)
234247
verify_rpc(slurm_errno())
235248
return self
236249

237-
def modify(self, Reservation changes):
250+
def modify(self, Reservation changes=None):
238251
"""Modify a Reservation.
239252
240253
Args:
241-
changes (pyslurm.Reservation):
254+
changes (pyslurm.Reservation, optional=None):
242255
Another Reservation object that contains all the changes to
243-
apply. Check the `Other Parameters` of the Reservation class to
244-
see which properties can be modified.
256+
apply. This is optional - you can also directly modify a
257+
Reservation object and just call `modify()`, and the changes
258+
will be sent to `slurmctld`.
245259
246260
Raises:
247261
(pyslurm.RPCError): When updating the Reservation was not
@@ -250,18 +264,27 @@ cdef class Reservation:
250264
Examples:
251265
>>> import pyslurm
252266
>>>
253-
>>> mynode = pyslurm.Node.load("localhost")
254-
>>> # Prepare the changes
255-
>>> changes = pyslurm.Node(state="DRAIN", reason="DRAIN Reason")
256-
>>> # Modify it
257-
>>> mynode.modify(changes)
267+
>>> resv = pyslurm.Reservation.load("maintenance")
268+
>>> # Add 60 Minutes to the reservation
269+
>>> resv.duration += 60
270+
>>>
271+
>>> # You can also add a slurm timestring.
272+
>>> # For example, extend the duration by another day:
273+
>>> resv.duration += pyslurm.utils.timestr_to_mins("1-00:00:00")
274+
>>>
275+
>>> # Now send the changes to the Controller:
276+
>>> resv.modify()
258277
"""
259-
if not changes.umsg:
278+
cdef Reservation updates = changes if changes is not None else self
279+
if not updates.umsg:
260280
return
261281

262282
self._error_or_name()
263-
cstr.fmalloc(&changes.umsg.name, self.info.name)
264-
verify_rpc(slurm_update_reservation(changes.umsg))
283+
cstr.fmalloc(&updates.umsg.name, self.info.name)
284+
verify_rpc(slurm_update_reservation(updates.umsg))
285+
286+
# Make sure we clean the object from any previous changes.
287+
updates._dealloc_umsg()
265288

266289
def delete(self):
267290
"""Delete a Reservation.
@@ -303,15 +326,15 @@ cdef class Reservation:
303326
cstr.fmalloc2(&self.info.comment, &self.umsg.comment, val)
304327

305328
@property
306-
def core_count(self):
329+
def cpus(self):
307330
return u32_parse(self.info.core_cnt, zero_is_noval=False)
308331

309-
@core_count.setter
310-
def core_count(self, val):
332+
@cpus.setter
333+
def cpus(self, val):
311334
self.info.core_cnt = self.umsg.core_cnt = int(val)
312335

313336
@property
314-
def cores_by_node(self):
337+
def cpus_by_node(self):
315338
out = {}
316339
for i in range(self.info.core_spec_cnt):
317340
node = cstr.to_unicode(self.info.core_spec[i].node_name)
@@ -410,7 +433,7 @@ cdef class Reservation:
410433
def duration(self):
411434
cdef time_t duration = 0
412435

413-
if self.info.end_time >= self.info.start_time:
436+
if self.start_time and self.info.end_time >= self.info.start_time:
414437
duration = <time_t>ctime.difftime(self.info.end_time,
415438
self.info.start_time)
416439

@@ -419,7 +442,8 @@ cdef class Reservation:
419442
@duration.setter
420443
def duration(self, val):
421444
val = timestr_to_mins(val)
422-
self.umsg.duration = val
445+
if not self.start_time:
446+
self.start_time = datetime.now()
423447
self.end_time = self.start_time + (val * 60)
424448

425449
@property
@@ -433,6 +457,11 @@ cdef class Reservation:
433457
def tres(self):
434458
return cstr.to_dict(self.info.tres_str)
435459

460+
@tres.setter
461+
def tres(self, val):
462+
cstr.fmalloc2(&self.info.tres_str, &self.umsg.tres_str,
463+
cstr.dict_to_str(val))
464+
436465
@property
437466
def users(self):
438467
return cstr.to_list(self.info.users)

0 commit comments

Comments
 (0)