Skip to content

Commit 0ea2d50

Browse files
committed
wip
1 parent db47ca6 commit 0ea2d50

File tree

10 files changed

+347
-31
lines changed

10 files changed

+347
-31
lines changed

pyslurm/db/account.pxd

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ from pyslurm.slurm cimport (
3131
slurmdb_account_cond_t,
3232
slurmdb_accounts_get,
3333
slurmdb_accounts_add,
34+
slurmdb_accounts_remove,
35+
slurmdb_accounts_modify,
3436
slurmdb_destroy_account_rec,
3537
slurmdb_destroy_account_cond,
3638
try_xmalloc,
@@ -72,6 +74,20 @@ cdef class AccountFilter:
7274

7375

7476
cdef class Account:
77+
"""Slurm Database Account.
78+
79+
Attributes:
80+
name (str):
81+
Name of the Account.
82+
description (str):
83+
Description of the Account.
84+
organization (str):
85+
Organization of the Account.
86+
is_deleted (bool):
87+
Whether this Account has been deleted or not.
88+
association (pyslurm.db.Association):
89+
This accounts association.
90+
"""
7591
cdef:
7692
slurmdb_account_rec_t *ptr
7793

pyslurm/db/account.pyx

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@
2222
# cython: c_string_type=unicode, c_string_encoding=default
2323
# cython: language_level=3
2424

25-
from pyslurm.core.error import RPCError, verify_rpc
25+
from pyslurm.core.error import RPCError, verify_rpc, slurm_errno
2626
from pyslurm.utils.helpers import (
2727
instance_to_dict,
2828
user_to_uid,
2929
)
3030
from pyslurm.utils.uint import *
3131
from pyslurm.db.connection import _open_conn_or_error
32-
from pyslurm import settings
3332
from pyslurm import xcollections
33+
from pyslurm.db.error import DefaultAccountError, JobsRunningError
3434

3535

3636
cdef class Accounts(dict):
@@ -113,6 +113,35 @@ cdef class Accounts(dict):
113113
# Autocommit if no connection was explicitly specified.
114114
conn.commit()
115115

116+
def delete(self, Connection db_connection=None):
117+
cdef:
118+
AccountFilter a_filter
119+
Connection conn
120+
SlurmList response
121+
list out = []
122+
123+
124+
a_filter = AccountFilter(names=list(self.keys()))
125+
a_filter._create()
126+
127+
conn = _open_conn_or_error(db_connection)
128+
response = SlurmList.wrap(slurmdb_accounts_remove(conn.ptr, a_filter.ptr))
129+
rc = slurm_errno()
130+
131+
if rc == slurm.SLURM_SUCCESS or rc == slurm.SLURM_NO_CHANGE_IN_DATA:
132+
return
133+
134+
# if rc == slurm.ESLURM_ACCESS_DENIED or response.is_null:
135+
# verify_rpc(rc)
136+
137+
# Handle the error cases.
138+
if rc == slurm.ESLURM_JOBS_RUNNING_ON_ASSOC:
139+
raise JobsRunningError.from_response(response, rc)
140+
elif rc == slurm.ESLURM_NO_REMOVE_DEFAULT_ACCOUNT:
141+
raise DefaultAccountError.from_response(response, rc)
142+
else:
143+
verify_rpc(rc)
144+
116145

117146
cdef class AccountFilter:
118147

@@ -169,8 +198,8 @@ cdef class Account:
169198
setattr(self, k, v)
170199

171200
def _init_defaults(self):
172-
self.cluster = settings.LOCAL_CLUSTER
173201
self.associations = []
202+
self.association = None
174203
self.coordinators = []
175204

176205
def __dealloc__(self):
@@ -209,7 +238,7 @@ cdef class Account:
209238

210239
def __eq__(self, other):
211240
if isinstance(other, Account):
212-
return self.name == other.name and self.cluster == other.cluster
241+
return self.name == other.name
213242
return NotImplemented
214243

215244
@property
@@ -241,5 +270,3 @@ cdef class Account:
241270
if self.ptr.flags & slurm.SLURMDB_ACCT_FLAG_DELETED:
242271
return True
243272
return False
244-
245-
# TODO: extract the association of this account

pyslurm/db/assoc.pxd

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ from pyslurm cimport slurm
2626
from pyslurm.slurm cimport (
2727
slurmdb_assoc_rec_t,
2828
slurmdb_assoc_cond_t,
29-
slurmdb_associations_get,
3029
slurmdb_destroy_assoc_rec,
3130
slurmdb_destroy_assoc_cond,
3231
slurmdb_init_assoc_rec,
32+
slurmdb_associations_get,
3333
slurmdb_associations_modify,
3434
slurmdb_associations_add,
35+
slurmdb_associations_remove,
3536
try_xmalloc,
3637
)
3738
from pyslurm.db.util cimport (

pyslurm/db/assoc.pyx

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
# cython: c_string_type=unicode, c_string_encoding=default
2323
# cython: language_level=3
2424

25-
from pyslurm.core.error import RPCError, verify_rpc
25+
from pyslurm.core.error import RPCError, verify_rpc, slurm_errno
2626
from pyslurm.utils.helpers import (
2727
instance_to_dict,
2828
user_to_uid,
@@ -31,6 +31,7 @@ from pyslurm.utils.uint import *
3131
from pyslurm.db.connection import _open_conn_or_error
3232
from pyslurm import settings
3333
from pyslurm import xcollections
34+
from pyslurm.db.error import JobsRunningError, DefaultAccountError
3435

3536

3637
cdef class AssociationList(SlurmList):
@@ -65,15 +66,6 @@ cdef class AssociationList(SlurmList):
6566
self.append(<Association>item)
6667

6768

68-
class AssociationModifyResponse:
69-
70-
def __init__(self, user=None, account=None, cluster=None, partition=None):
71-
self.user = user
72-
self.account = account
73-
self.cluster = cluster
74-
self.partition = partition
75-
76-
7769
cdef class Associations(MultiClusterMap):
7870

7971
def __init__(self, assocs=None):
@@ -201,6 +193,39 @@ cdef class Associations(MultiClusterMap):
201193
# Autocommit if no connection was explicitly specified.
202194
conn.commit()
203195

196+
def delete(self, Connection db_connection):
197+
cdef:
198+
AssociationFilter afilter
199+
Connection conn
200+
SlurmList response
201+
SlurmListItem response_ptr
202+
203+
ids = [assoc.id for assoc in self.values()]
204+
if not ids:
205+
return
206+
207+
a_filter = AssociationFilter(ids=ids)
208+
a_filter._create()
209+
210+
conn = _open_conn_or_error(db_connection)
211+
response = SlurmList.wrap(slurmdb_associations_remove(conn.ptr,
212+
a_filter.ptr))
213+
rc = slurm_errno()
214+
215+
if rc == slurm.SLURM_SUCCESS or rc == slurm.SLURM_NO_CHANGE_IN_DATA:
216+
return
217+
218+
#if rc == slurm.ESLURM_ACCESS_DENIED or response.is_null:
219+
# verify_rpc(rc)
220+
221+
# Handle the error cases.
222+
if rc == slurm.ESLURM_JOBS_RUNNING_ON_ASSOC:
223+
raise JobsRunningError.from_response(response, rc)
224+
elif rc == slurm.ESLURM_NO_REMOVE_DEFAULT_ACCOUNT:
225+
raise DefaultAccountError.from_response(response, rc)
226+
else:
227+
verify_rpc(rc)
228+
204229

205230
cdef class AssociationFilter:
206231

pyslurm/db/error.pyx

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
#########################################################################
2+
# error.pyx - pyslurm db specific errors
3+
#########################################################################
4+
# Copyright (C) 2025 Toni Harzendorf <[email protected]>
5+
#
6+
# This file is part of PySlurm
7+
#
8+
# PySlurm is free software; you can redistribute it and/or modify
9+
# it under the terms of the GNU General Public License as published by
10+
# the Free Software Foundation; either version 2 of the License, or
11+
# (at your option) any later version.
12+
13+
# PySlurm is distributed in the hope that it will be useful,
14+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
# GNU General Public License for more details.
17+
#
18+
# You should have received a copy of the GNU General Public License along
19+
# with PySlurm; if not, write to the Free Software Foundation, Inc.,
20+
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21+
#
22+
# cython: c_string_type=unicode, c_string_encoding=default
23+
# cython: language_level=3
24+
25+
from pyslurm.core.error import RPCError, slurm_errno, verify_rpc
26+
from pyslurm.db.util cimport SlurmList, SlurmListItem
27+
28+
29+
class AssociationChangeInfo:
30+
31+
def __init__(self, user, cluster, account, partition=None):
32+
self.user = user
33+
self.cluster = cluster
34+
self.account = account
35+
self.partition = partition
36+
self.running_jobs = []
37+
38+
39+
class JobsRunningError(RPCError):
40+
41+
def __init__(self, **kwargs):
42+
super().__init__(**kwargs)
43+
self.associations = []
44+
45+
@staticmethod
46+
def from_response(SlurmList response, rc):
47+
running_jobs = parse_running_job_errors(response)
48+
err = JobsRunningError(errno=rc)
49+
err.associations = list(running_jobs.values())
50+
return err
51+
52+
53+
class DefaultAccountError(RPCError):
54+
55+
def __init__(self, **kwargs):
56+
super().__init__(**kwargs)
57+
self.associations = []
58+
59+
@staticmethod
60+
def from_response(SlurmList response, rc):
61+
err = DefaultAccountError(errno=rc)
62+
err.associations = parse_default_account_errors(response)
63+
return err
64+
65+
66+
def parse_default_account_errors(SlurmList response):
67+
cdef SlurmListItem response_ptr
68+
69+
assocs = []
70+
for response_ptr in response:
71+
response_str = response_ptr.to_str()
72+
if not response_str:
73+
continue
74+
75+
# The response is a string in the following form:
76+
# C = X A = X U = X P = X
77+
#
78+
# And we have to parse this stuff... The Partition (P) is optional
79+
resp = response_str.rstrip().lstrip()
80+
splitted = resp.split(" ")
81+
values = []
82+
for item in splitted:
83+
if not item:
84+
continue
85+
86+
key, value = item.split("=")
87+
values.append(value.strip())
88+
89+
info = AssociationChangeInfo(
90+
cluster = values[0],
91+
account = values[1],
92+
user = values[2],
93+
)
94+
assoc_str = f"{info.cluster}-{info.account}-{info.user}"
95+
96+
if len(values) > 3:
97+
info.partition = values[3]
98+
assoc_str = f"{assoc_str}-{info.partition}"
99+
100+
assocs.append(info)
101+
102+
return assocs
103+
104+
105+
def parse_running_job_errors(SlurmList response):
106+
cdef SlurmListItem response_ptr
107+
108+
running_jobs_for_assoc = {}
109+
for response_ptr in response:
110+
response_str = response_ptr.to_str()
111+
if not response_str:
112+
continue
113+
114+
# The response is a string in the following form:
115+
# JobId = X C = X A = X U = X P = X
116+
#
117+
# And we have to parse this stuff... The Partition (P) is optional
118+
resp = response_str.rstrip().lstrip()
119+
splitted = resp.split(" ")
120+
values = []
121+
for item in splitted:
122+
if not item:
123+
continue
124+
125+
key, value = item.split("=")
126+
values.append(value.strip())
127+
128+
job_id = int(values[0])
129+
cluster = values[1]
130+
account = values[2]
131+
user = values[3]
132+
partition = None
133+
assoc_str = f"{cluster}-{account}-{user}"
134+
135+
if len(values) > 4:
136+
partition = values[4]
137+
assoc_str = f"{assoc_str}-{partition}"
138+
139+
if assoc_str not in running_jobs_for_assoc:
140+
info = AssociationChangeInfo(
141+
user = user,
142+
cluster = cluster,
143+
account = account,
144+
partition = partition,
145+
)
146+
running_jobs_for_assoc[assoc_str] = info
147+
148+
running_jobs_for_assoc[assoc_str].running_jobs.append(job_id)
149+
150+
return running_jobs_for_assoc

pyslurm/db/qos.pxd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ from pyslurm.db.util cimport (
4040
)
4141
from pyslurm.db.connection cimport Connection
4242
from pyslurm.utils cimport cstr
43-
from pyslurm.utils.uint cimport u16_set_bool_flag
43+
from pyslurm.utils.uint cimport u16_set_bool_flag, u32, u32_parse
4444

4545
cdef _set_qos_list(list_t **in_list, vals, QualitiesOfService data)
4646

0 commit comments

Comments
 (0)