blob: 8d150afda23e15f208c6c5e96bbcd0b2b2824411 [file] [edit]
############################################################################
# Copyright (C) SchedMD LLC.
############################################################################
import pytest
import atf
test_user = atf.properties["test-user"]
slurm_user = atf.properties["slurm-user"]
test_acct = "test_acct"
extra_acct = "extra_acct"
parent_acct = "parent_acct"
restricted_acct = "restricted_acct"
TEST_CASES = [
dict(
name="user_match1",
res_account=None,
res_user=test_user,
expect_invalid_res=False,
expect_show_res=True,
expect_job_in_res=True,
),
dict(
name="user_match2",
res_account=None,
res_user=f"{test_user},{slurm_user}",
expect_invalid_res=False,
expect_show_res=True,
expect_job_in_res=True,
),
dict(
name="user_match3",
res_account=None,
res_user=f"{slurm_user},{test_user}",
expect_invalid_res=False,
expect_show_res=True,
expect_job_in_res=True,
),
dict(
name="no_user_match",
res_account=None,
res_user=slurm_user,
expect_invalid_res=False,
expect_show_res=False,
expect_job_in_res=False,
),
dict(
name="account_match1",
res_account=test_acct,
res_user=None,
expect_invalid_res=False,
expect_show_res=True,
expect_job_in_res=True,
),
dict(
name="account_match2",
res_account=f"{test_acct},{restricted_acct}",
res_user=None,
expect_invalid_res=False,
expect_show_res=True,
expect_job_in_res=True,
),
dict(
name="account_match3",
res_account=f"{restricted_acct},{test_acct}",
res_user=None,
expect_invalid_res=False,
expect_show_res=True,
expect_job_in_res=True,
),
dict(
name="no_account_match",
res_account=restricted_acct,
res_user=None,
expect_invalid_res=False,
expect_show_res=False,
expect_job_in_res=False,
),
dict(
name="parent_account_match",
res_account=parent_acct,
res_user=None,
expect_invalid_res=False,
expect_show_res=True,
expect_job_in_res=True,
),
dict(
name="deny_user1",
res_account=None,
res_user=f"-{test_user}",
expect_invalid_res=False,
expect_show_res=False,
expect_job_in_res=False,
),
dict(
name="deny_user2",
res_account=None,
res_user=f"-{test_user},-{slurm_user}",
expect_invalid_res=False,
expect_show_res=False,
expect_job_in_res=False,
),
dict(
name="deny_user3",
res_account=None,
res_user=f"-{slurm_user}",
expect_invalid_res=False,
expect_show_res=True,
expect_job_in_res=True,
),
dict(
name="acct_match_deny_user",
res_account=test_acct,
res_user=f"-{test_user}",
expect_invalid_res=False,
expect_show_res=False,
expect_job_in_res=False,
),
dict(
name="deny_acct",
res_account=f"-{test_acct}",
res_user=None,
expect_invalid_res=False,
expect_show_res=False,
expect_job_in_res=False,
),
dict(
name="user_match_deny_acct",
res_account=f"-{test_acct}",
res_user=test_user,
expect_invalid_res=False,
expect_show_res=False,
expect_job_in_res=False,
),
dict(
name="mixed_access_invalid_res1",
res_account=None,
res_user=f"-{test_user},{slurm_user}",
expect_invalid_res=True,
),
dict(
name="mixed_access_invalid_res2",
res_account=None,
res_user=f"{test_user},-{slurm_user}",
expect_invalid_res=True,
),
dict(
name="mixed_access_invalid_res3",
res_account=f"{test_acct},-{restricted_acct}",
res_user=None,
expect_invalid_res=True,
),
dict(
name="mixed_access_invalid_res4",
res_account=f"-{test_acct},{restricted_acct}",
res_user=None,
expect_invalid_res=True,
),
]
@pytest.fixture(scope="module", autouse=True)
def setup():
if test_user == slurm_user:
pytest.skip(
f"This test requires SlurmUser ({slurm_user}) and SlurmTestUser ({test_user}) to be different.",
allow_module_level=True,
)
atf.require_accounting(modify=True)
atf.require_config_parameter_includes("PrivateData", "reservations")
atf.require_config_parameter_includes("AccountingStorageEnforce", "associations")
atf.require_slurm_running()
@pytest.fixture(scope="module", autouse=True)
def setup_db():
accounts = [
f"{parent_acct}",
f"{restricted_acct}",
f"{test_acct} parent={parent_acct}",
f"{extra_acct}",
]
users = [
f"{slurm_user} account={restricted_acct},{extra_acct}",
f"{test_user} account={test_acct}",
]
try:
for account in accounts:
atf.run_command(
f"sacctmgr -i add account {account}",
user=slurm_user,
fatal=True,
)
for user in users:
atf.run_command(
f"sacctmgr -i add user {user}",
user=slurm_user,
fatal=True,
)
atf.run_command("sacctmgr show assoc tree", user=slurm_user, fatal=True)
yield
finally:
atf.run_command("sacctmgr show assoc tree", user=slurm_user, fatal=True)
for user in reversed(users):
atf.run_command(
f"sacctmgr -i delete user {user}",
user=slurm_user,
fatal=False,
)
for account in reversed(accounts):
atf.run_command(
f"sacctmgr -i delete account {account}",
user=slurm_user,
fatal=False,
)
@pytest.fixture
def reservation(request):
try:
case = request.param
res_name = f"res_{case['name']}"
# Create reservation
res_args = [
f"reservationname={res_name}",
"start=now",
"duration=10",
"nodecnt=1",
]
if case.get("res_user"):
res_args.append(f"user={case['res_user']}")
if case.get("res_account"):
res_args.append(f"account={case['res_account']}")
create_cmd = f"scontrol create {' '.join(res_args)}"
result = atf.run_command(create_cmd, user=slurm_user)
if case["expect_invalid_res"]:
assert (
result["exit_code"] != 0
), f"Should not be able to create invalid reservation {res_name}"
res_name = None
else:
assert result["exit_code"] == 0, f"Couldn't create reservation {res_name}"
# Yield values for the test
yield case, res_name
finally:
atf.cancel_all_jobs(fatal=True)
if res_name:
atf.run_command(
f"scontrol delete reservationname={res_name}", user=slurm_user
)
def show_as_slurm_user(res_name):
show = atf.run_command(f"scontrol show reservation {res_name}", user=slurm_user)
assert (
f"ReservationName={res_name} StartTime=" in show["stdout"]
), "Reservation should always be visible as SlurmUser"
def show_as_test_user(case, res_name):
show = atf.run_command(f"scontrol show reservation {res_name}", user=test_user)
if case["expect_show_res"]:
assert (
f"ReservationName={res_name} StartTime=" in show["stdout"]
), "Reservation should be visible"
else:
assert (
f"ReservationName={res_name} StartTime=" not in show["stdout"]
), "Reservation should not be visible"
def run_job_as_slurm_user(res_name):
run = atf.run_command(
f"srun -N1 --account={restricted_acct} --reservation={res_name} true",
user=slurm_user,
)
assert run["exit_code"] == 0, "Job should have been allowed in reservation"
def run_job_as_test_user(case, res_name):
run = atf.run_command(
f"srun -N1 --account={test_acct} --reservation={res_name} true", user=test_user
)
if case["expect_job_in_res"]:
assert run["exit_code"] == 0, "Job should have been allowed in reservation"
else:
assert run["exit_code"] != 0, "Job should have been denied for reservation"
assert "Access denied" in run["stderr"]
@pytest.mark.parametrize(
"reservation", TEST_CASES, indirect=True, ids=lambda c: c["name"]
)
def test_reservation_access(reservation):
case, res_name = reservation
if res_name is None:
return
show_as_slurm_user(res_name)
show_as_test_user(case, res_name)
run_job_as_slurm_user(res_name)
run_job_as_test_user(case, res_name)