blob: b82a59cbee11916a1228833585c6e23f2682e685 [file] [log] [blame]
############################################################################
# Copyright (C) SchedMD LLC.
############################################################################
import pytest
import atf
import re
import logging
node1 = ""
node2 = ""
@pytest.fixture(scope="module", autouse=True)
def setup():
atf.require_accounting(modify=True)
atf.require_nodes(2)
atf.require_slurm_running()
@pytest.fixture(scope="module", autouse=False)
def node_list():
return atf.run_job_nodes("-N2 true")
@pytest.fixture(scope="function")
def create_resv(request, node_list):
global node1, node2
node1, node2 = node_list
flag = request.param
atf.run_command(
"scontrol create reservation reservationname=resv1 "
f"user={atf.properties['test-user']} start=now duration=1 nodecnt=1 "
f"flags={flag}",
user=atf.properties["slurm-user"],
fatal=True,
)
if re.search(
rf"(?:Nodes=)({node2})", atf.run_command_output("scontrol show res resv1")
):
node_list.reverse()
node1, node2 = node_list
return [node1, node2, flag]
@pytest.fixture(scope="function")
def delete_resv():
yield
atf.run_command(
f"scontrol update nodename={node1},{node2} state=idle reason='testing'",
user=atf.properties["slurm-user"],
)
atf.run_command(
"scontrol delete reservation resv1", user=atf.properties["slurm-user"]
)
@pytest.mark.parametrize("create_resv", ["REPLACE_DOWN", "REPLACE", ""], indirect=True)
def test_replace_flags(create_resv, delete_resv):
"""Verify that nodes in a reservation with flags replace and replace_down
are replaced when they go down
"""
node1, node2, flag = create_resv
logging.info(f"Assert that resv1 initially has {node1}")
assert (
re.search(
rf"(?:Nodes=)({node1})",
atf.run_command_output("scontrol show reservationname resv1"),
)
is not None
)
logging.info(f"Assert that {node1} has the +RESERVED state")
assert atf.repeat_command_until(
f"scontrol show node {node1}",
lambda results: re.search(r"(?:State=).+(\+RESERVED)", results["stdout"]),
quiet=False,
)
logging.info(f"Set {node1} down")
atf.run_command(
f"scontrol update nodeName={node1} state=down reason='testing'",
user=atf.properties["slurm-user"],
fatal=True,
)
logging.info(f"Assert that resv1 now has {node2}")
assert atf.repeat_command_until(
"scontrol show reservationname resv1",
lambda results: re.search(rf"(?:Nodes=)({node2})", results["stdout"]),
quiet=False,
)
logging.info(f"Assert that {node2} now has the +RESERVED state")
assert atf.repeat_command_until(
f"scontrol show node {node2}",
lambda results: re.search(r"(?:State=).+(\+RESERVED)", results["stdout"]),
quiet=False,
)
logging.info(f"Assert that {node1} has NOT the +RESERVED state anymore")
assert atf.repeat_command_until(
f"scontrol show node {node1}",
lambda results: not re.search(r"(?:State=).+(\+RESERVED)", results["stdout"]),
quiet=False,
)
@pytest.mark.parametrize("create_resv", ["STATIC_ALLOC", "MAINT"], indirect=True)
def test_noreplace_flags(create_resv, delete_resv):
"""Verify that nodes in a reservation with static allocations aren't
replaced when they go down
"""
node1, node2, flag = create_resv
logging.info(f"Assert that resv1 initially has {node1}")
assert (
re.search(
rf"(?:Nodes=)({node1})",
atf.run_command_output("scontrol show reservationname resv1"),
)
is not None
)
logging.info(f"Assert that {node1} has the +RESERVED state")
assert atf.repeat_command_until(
f"scontrol show node {node1}",
lambda results: re.search(r"(?:State=).+(\+RESERVED)", results["stdout"]),
quiet=False,
)
if flag == "MAINT":
logging.info(f"Assert that {node1} has the +MAINT state")
assert atf.repeat_command_until(
f"scontrol show node {node1}",
lambda results: re.search(r"(?:State=).+(\+MAINT)", results["stdout"]),
quiet=False,
)
logging.info(f"Set {node1} down")
atf.run_command(
f"scontrol update nodeName={node1} state=down reason='testing'",
user=atf.properties["slurm-user"],
fatal=True,
)
logging.info(f"Assert that resv1 won't have {node2}")
assert not atf.repeat_command_until(
"scontrol show reservationname resv1",
lambda results: re.search(rf"(?:Nodes=)({node2})", results["stdout"]),
timeout=15,
quiet=False,
)
logging.info(f"Assert that {node2} doesn't get the +RESERVED state")
assert not atf.repeat_command_until(
f"scontrol show node {node2}",
lambda results: re.search(r"(?:State=).+(\+RESERVED)", results["stdout"]),
timeout=15,
quiet=False,
)
if flag == "MAINT":
logging.info(f"Assert that {node1} still has the +MAINT state")
assert atf.repeat_command_until(
f"scontrol show node {node1}",
lambda results: re.search(r"(?:State=).+(\+MAINT)", results["stdout"]),
quiet=False,
)
@pytest.mark.parametrize(
"flag, delete_resv",
[
("MAINT,REPLACE", True),
("STATIC_ALLOC,REPLACE", True),
("MAINT,REPLACE_DOWN", True),
("STATIC_ALLOC,REPLACE_DOWN", True),
("REPLACE,MAINT", True),
("REPLACE_DOWN,MAINT", True),
("REPLACE,STATIC_ALLOC", True),
("REPLACE_DOWN,STATIC_ALLOC", True),
("HOURLY,WEEKDAY", True),
("WEEKDAY,WEEKEND", True),
("WEEKEND,WEEKLY", True),
("WEEKLY,HOURLY", True),
("TIME_FLOAT,HOURLY", True),
("TIME_FLOAT,WEEKDAY", True),
("TIME_FLOAT,WEEKEND", True),
("TIME_FLOAT,WEEKLY", True),
("HOURLY,TIME_FLOAT", True),
("WEEKDAY,TIME_FLOAT", True),
("WEEKEND,TIME_FLOAT", True),
("WEEKLY,TIME_FLOAT", True),
],
)
def test_incomp_flags(flag, delete_resv):
"""Verify that reservations are not allowed to be created with incompatible
flags"""
logging.info(f"Attempt to create a reservation with incompatible flags: {flag}")
result = atf.run_command(
"scontrol create reservationname=resv1 "
f"user={atf.properties['test-user']} start=now duration=1 nodecnt=1 "
f"flags={flag}",
user=atf.properties["slurm-user"],
xfail=True,
fatal=True,
)
expected_output = "Error creating the reservation"
logging.info(f"Assert output message contains {expected_output}")
assert (
expected_output in result["stderr"]
), f"Could not find {expected_output} in {result['stderr']}"
logging.info("Assert exit code is not 0")
assert result["exit_code"] != 0, "The command was supposed to fail, but didn't!"
@pytest.mark.parametrize(
"create_flag, update_flag",
[
("MAINT", "REPLACE"),
("STATIC_ALLOC", "REPLACE"),
("MAINT", "REPLACE_DOWN"),
("STATIC_ALLOC", "REPLACE_DOWN"),
("REPLACE", "MAINT"),
("REPLACE_DOWN", "MAINT"),
("REPLACE", "STATIC_ALLOC"),
("REPLACE_DOWN", "STATIC_ALLOC"),
("HOURLY", "WEEKDAY"),
("WEEKDAY", "WEEKEND"),
("WEEKEND", "WEEKLY"),
("WEEKLY", "HOURLY"),
("TIME_FLOAT", "HOURLY"),
("TIME_FLOAT", "WEEKDAY"),
("TIME_FLOAT", "WEEKEND"),
("TIME_FLOAT", "WEEKLY"),
("HOURLY", "TIME_FLOAT"),
("WEEKDAY", "TIME_FLOAT"),
("WEEKEND", "TIME_FLOAT"),
("WEEKLY", "TIME_FLOAT"),
],
)
def test_update_incomp_flags(create_flag, update_flag, delete_resv):
"""Verify that reservations created with a a given flag cannot be
updated with incompatible flags"""
result = atf.run_command(
"scontrol create reservationname=resv1 "
f"user={atf.properties['test-user']} start=now duration=1 nodecnt=1 "
f"flags={create_flag}",
user=atf.properties["slurm-user"],
)
result = atf.run_command(
f"scontrol update reservation=resv1 flags={update_flag}",
user=atf.properties["slurm-user"],
xfail=True,
fatal=True,
)
expected_output = "slurm_update error"
logging.info(f"Assert output message contains {expected_output}")
assert (
expected_output in result["stderr"]
), "Could not find 'error updating the res' in output"
logging.info("Assert exit code is not 0")
assert result["exit_code"] != 0, "The command was supposed to fail, but didn't!"