Skip to content

Commit fb427f7

Browse files
Merge pull request #109 from NessieCanCode/update-accounts-dropdown-in-settings
List all cluster accounts in settings
2 parents 86e1f92 + 60c5a54 commit fb427f7

File tree

4 files changed

+127
-5
lines changed

4 files changed

+127
-5
lines changed

src/slurmcostmanager.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -892,14 +892,10 @@ function Rates({ onRatesUpdated }) {
892892
try {
893893
let json;
894894
if (window.cockpit && window.cockpit.spawn) {
895-
const { start, end } = getBillingPeriod();
896895
const args = [
897896
'python3',
898897
`${PLUGIN_BASE}/slurmdb.py`,
899-
'--start',
900-
start,
901-
'--end',
902-
end,
898+
'--accounts',
903899
'--output',
904900
'-',
905901
];

src/slurmdb.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,38 @@ def aggregate_usage(self, start_time, end_time):
452452
job_entry['core_hours'] += cpus * dur_hours
453453
return agg, totals
454454

455+
def fetch_all_accounts(self):
456+
"""Return a sorted list of all accounts in the cluster."""
457+
self.connect()
458+
accounts = set()
459+
with self._conn.cursor() as cur:
460+
try:
461+
cur.execute("SHOW TABLES LIKE 'acct_table'")
462+
if cur.fetchone():
463+
cur.execute("SELECT name FROM acct_table WHERE deleted = 0")
464+
for row in cur.fetchall():
465+
name = row.get('name')
466+
if name:
467+
accounts.add(name)
468+
return sorted(accounts)
469+
except pymysql.err.ProgrammingError:
470+
pass
471+
472+
assoc_table = (
473+
f"{self.cluster}_assoc_table" if self.cluster else "assoc_table"
474+
)
475+
try:
476+
cur.execute(
477+
f"SELECT DISTINCT acct FROM {assoc_table} WHERE deleted = 0"
478+
)
479+
for row in cur.fetchall():
480+
acct = row.get('acct')
481+
if acct:
482+
accounts.add(acct)
483+
except pymysql.err.ProgrammingError:
484+
pass
485+
return sorted(accounts)
486+
455487
def fetch_invoices(self, start_date=None, end_date=None):
456488
"""Fetch invoice metadata from the database if present."""
457489
if start_date:
@@ -651,6 +683,7 @@ def export_summary(self, start_time, end_time):
651683
dest="slurm_conf",
652684
help="path to slurm.conf for auto cluster detection",
653685
)
686+
parser.add_argument("--accounts", action="store_true", help="list all accounts and exit")
654687

655688
args = parser.parse_args()
656689

@@ -660,6 +693,17 @@ def export_summary(self, start_time, end_time):
660693
slurm_conf=args.slurm_conf,
661694
)
662695

696+
if args.accounts:
697+
data = {"accounts": db.fetch_all_accounts()}
698+
if args.output in ("-", "/dev/stdout"):
699+
json.dump(data, sys.stdout, indent=2, default=str)
700+
sys.stdout.write("\n")
701+
else:
702+
with open(args.output, "w") as fh:
703+
json.dump(data, fh, indent=2, default=str)
704+
print(f"Wrote {args.output}")
705+
sys.exit(0)
706+
663707
def _export_day(day):
664708
day_str = day.isoformat()
665709
data = db.export_summary(day_str, day_str)

test/check-application

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ PYTHONPATH=src python test/unit/slurm_schema_dump.test.py
1010
PYTHONPATH=src python test/unit/billing_summary.test.py
1111
PYTHONPATH=src python test/unit/invoice_retrieval.test.py
1212
PYTHONPATH=src python test/unit/auth_boundaries.test.py
13+
PYTHONPATH=src python test/unit/accounts_listing.test.py

test/unit/accounts_listing.test.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import unittest
2+
from slurmdb import SlurmDB
3+
4+
5+
class AccountListingTests(unittest.TestCase):
6+
def test_fetch_accounts_from_acct_table(self):
7+
db = SlurmDB()
8+
db.connect = lambda: None
9+
10+
class FakeCursor:
11+
def __init__(self):
12+
self.last_query = ""
13+
14+
def __enter__(self):
15+
return self
16+
17+
def __exit__(self, exc_type, exc, tb):
18+
pass
19+
20+
def execute(self, query, params=None):
21+
self.last_query = query
22+
23+
def fetchone(self):
24+
if "SHOW TABLES LIKE" in self.last_query:
25+
return {"name": "acct_table"}
26+
return None
27+
28+
def fetchall(self):
29+
if "SELECT name FROM acct_table" in self.last_query:
30+
return [{"name": "acct1"}, {"name": "acct2"}]
31+
return []
32+
33+
class FakeConn:
34+
def cursor(self):
35+
return FakeCursor()
36+
37+
db._conn = FakeConn()
38+
accounts = db.fetch_all_accounts()
39+
self.assertEqual(accounts, ["acct1", "acct2"])
40+
41+
def test_fetch_accounts_from_assoc_table_when_acct_table_missing(self):
42+
db = SlurmDB(cluster="localcluster")
43+
db.connect = lambda: None
44+
45+
class FakeCursor:
46+
def __init__(self):
47+
self.last_query = ""
48+
49+
def __enter__(self):
50+
return self
51+
52+
def __exit__(self, exc_type, exc, tb):
53+
pass
54+
55+
def execute(self, query, params=None):
56+
self.last_query = query
57+
58+
def fetchone(self):
59+
# acct_table does not exist
60+
return None
61+
62+
def fetchall(self):
63+
if "localcluster_assoc_table" in self.last_query:
64+
return [
65+
{"acct": "acct1"},
66+
{"acct": "acct2"},
67+
{"acct": "acct1"},
68+
]
69+
return []
70+
71+
class FakeConn:
72+
def cursor(self):
73+
return FakeCursor()
74+
75+
db._conn = FakeConn()
76+
accounts = db.fetch_all_accounts()
77+
self.assertEqual(accounts, ["acct1", "acct2"])
78+
79+
80+
if __name__ == "__main__":
81+
unittest.main()

0 commit comments

Comments
 (0)