| ############################################################################ | |
| # Copyright (C) SchedMD LLC. | |
| ############################################################################ | |
| import re | |
| import pytest | |
| import atf | |
| # Global variables | |
| cluster1 = "cluster1" | |
| cluster2 = "cluster2" | |
| cluster3 = "cluster3" | |
| acct1 = "acct1" | |
| acct2 = "acct2" | |
| acct3 = "acct3" | |
| acct4 = "acct4" | |
| acct5 = "acct4_sub" | |
| acct6 = "acct4_sub_sub" | |
| acct7 = "acct5" | |
| acct8 = "acct6" | |
| acct9 = "acct7" | |
| user1 = "user1" | |
| user2 = "user2" | |
| user3 = "user3" | |
| part1 = "part1" | |
| part2 = "part2" | |
| part3 = "part3" | |
| part4 = "part4" | |
| defacct = acct1 | |
| @pytest.fixture(scope="module", autouse=True) | |
| def setup(): | |
| """Test setup with required configurations.""" | |
| atf.require_accounting(modify=True) | |
| atf.require_config_parameter_includes("AccountingStorageEnforce", "associations") | |
| atf.require_config_parameter("AllowNoDefAcct", "No", source="slurmdbd") | |
| # these can be helpful when debugging/tracing | |
| # atf.require_config_parameter("DebugLevel", "debug4", source="slurmdbd") | |
| # atf.require_config_parameter("DebugFlags", f"{atf.get_config(live=False, source="slurmdbd", quiet=True)["DebugFlags"]},DB_ASSOC", source="slurmdbd") | |
| atf.require_slurm_running() | |
| @pytest.fixture(scope="function", autouse=True) | |
| def setup_db(): | |
| # Create test clusters and accounts | |
| atf.run_command( | |
| f"sacctmgr -i add cluster {cluster1},{cluster2},{cluster3}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| atf.run_command( | |
| f"sacctmgr -i add account {acct1},{acct2},{acct3},{acct4} cluster={cluster1},{cluster2},{cluster3}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| atf.run_command( | |
| f"sacctmgr -i add user {user1} DefaultAccount={defacct} account={acct1},{acct2},{acct3},{acct4} cluster={cluster1},{cluster2},{cluster3}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| yield | |
| atf.run_command( | |
| f"sacctmgr -i remove user {user1} acct={acct1},{acct2},{acct3},{acct4}", | |
| user=atf.properties["slurm-user"], | |
| quiet=True, | |
| ) | |
| atf.run_command( | |
| f"sacctmgr -i remove account {acct1},{acct2},{acct3},{acct4}", | |
| user=atf.properties["slurm-user"], | |
| quiet=True, | |
| ) | |
| atf.run_command( | |
| f"sacctmgr -i remove cluster {cluster1},{cluster2},{cluster3}", | |
| user=atf.properties["slurm-user"], | |
| quiet=True, | |
| ) | |
| def test_account_removal(): | |
| """Test removing an account: | |
| - Verify that removing default account for a user fails. | |
| """ | |
| # Try to remove a user's default account | |
| result = atf.run_command( | |
| f"sacctmgr -i remove account {defacct} cluster={cluster1},{cluster2},{cluster3}", | |
| user=atf.properties["slurm-user"], | |
| xfail=True, | |
| ) | |
| assert result["exit_code"] != 0, "Removing default account should fail" | |
| assert ( | |
| "can not remove the default account of a user" in result["stderr"] | |
| ), 'sacctmgr should explain that we "can not remove the default account of a user"' | |
| # Try to remove subset (including default) of accounts of user on cluster1 | |
| result = atf.run_command( | |
| f"sacctmgr -i remove account {defacct},{acct2} cluster={cluster1}", | |
| user=atf.properties["slurm-user"], | |
| xfail=True, | |
| ) | |
| assert ( | |
| result["exit_code"] != 0 | |
| ), "Removing default account, either with other accounts, should fail" | |
| assert ( | |
| "can not remove the default account of a user" in result["stderr"] | |
| ), 'sacctmgr should explain that we "can not remove the default account of a user"' | |
| # Make user have only one account, the default on cluster1 | |
| atf.run_command( | |
| f"sacctmgr -i remove account {acct2},{acct3},{acct4} cluster={cluster1}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| # Try to remove default account on all clusters | |
| result = atf.run_command( | |
| f"sacctmgr -i remove account {defacct}", | |
| user=atf.properties["slurm-user"], | |
| xfail=True, | |
| ) | |
| assert ( | |
| result["exit_code"] != 0 | |
| ), "Removing default account when it's the only one account should fail" | |
| assert ( | |
| "can not remove the default account of a user" in result["stderr"] | |
| ), 'sacctmgr should explain that we "can not remove the default account of a user"' | |
| # Make acct2 have acct1 as parent | |
| atf.run_command( | |
| f"sacctmgr -i modify account {acct2} cluster={cluster1},{cluster2},{cluster3} set parent={acct1}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| # Again, try to remove default account on all clusters | |
| result = atf.run_command( | |
| f"sacctmgr -i remove account {defacct}", | |
| user=atf.properties["slurm-user"], | |
| xfail=True, | |
| ) | |
| assert ( | |
| result["exit_code"] != 0 | |
| ), "Removing default account, either when it's a parent, should fail" | |
| assert ( | |
| "can not remove the default account of a user" in result["stderr"] | |
| ), 'sacctmgr should explain that we "can not remove the default account of a user"' | |
| # Remove all accounts of user on cluster2 | |
| atf.run_command( | |
| f"sacctmgr -i remove user {user1} acct={acct2},{acct3},{acct4} cluster={cluster2}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| # Remove accounts acct2,acct3,acct4 from user on remaining clusters | |
| atf.run_command( | |
| f"sacctmgr -i remove user {user1} acct={acct2},{acct3},{acct4}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| # Again, try to remove default account on all clusters since it is now user's only account | |
| result = atf.run_command( | |
| f"sacctmgr -i remove account {defacct}", | |
| user=atf.properties["slurm-user"], | |
| ) | |
| assert ( | |
| result["exit_code"] == 0 | |
| ), "Removing default account when it's not a default account should succeed" | |
| assert defacct in result["stdout"], "sacctmgr should report the deleted account" | |
| @pytest.mark.xfail( | |
| atf.get_version("sbin/slurmdbd") < (24, 11, 4) | |
| or ( | |
| (25, 5) <= atf.get_version("sbin/slurmdbd") | |
| and atf.get_version("sbin/slurmdbd") < (25, 11) | |
| ), | |
| reason="Ticket 24228: In 24.11.4 we fixed an issue when removing an account specifying the parent", | |
| ) | |
| def test_account_removal_with_parent(): | |
| """Test removing an account using parent=""" | |
| # Add a sub acct | |
| atf.run_command( | |
| f"sacctmgr -i add account {acct5} parent={acct4} cluster={cluster1}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| # Add another sub acct | |
| atf.run_command( | |
| f"sacctmgr -i add account {acct6} parent={acct5} cluster={cluster1}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| # Now add user to it | |
| atf.run_command( | |
| f"sacctmgr -i add user {user1} acct={acct5} cluster={cluster1}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| # Remove non-default account with parent specified | |
| result = atf.run_command( | |
| f"sacctmgr -i remove account {acct5} parent={acct4} cluster={cluster1}", | |
| user=atf.properties["slurm-user"], | |
| ) | |
| assert ( | |
| result["exit_code"] == 0 | |
| ), "Removing non-default account with a parent should succeed" | |
| assert ( | |
| f"A = {acct5}" in result["stdout"] | |
| ), "sacctmgr should report the deleted account" | |
| # Confirm previous operation succeeded | |
| output = atf.run_command_output( | |
| f"sacctmgr -n show assoc format=acct,user,lineage acct={acct5},{acct6} cluster={cluster1}", | |
| fatal=True, | |
| ) | |
| assert ( | |
| output == "" | |
| ), f"User {user1} with acct {acct5} shouldn't have an assoc anymore" | |
| def test_account_removal_with_partition(): | |
| """Test removing an account using partition=""" | |
| # Add user to non-def account with partition | |
| atf.run_command( | |
| f"sacctmgr -i add user {user1} acct={acct4} partition={part1} cluster={cluster1}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| # Remove account with partition specified | |
| result = atf.run_command( | |
| f"sacctmgr -i remove acct {acct4} partition={part1} cluster={cluster1}", | |
| user=atf.properties["slurm-user"], | |
| ) | |
| assert result["exit_code"] == 0, "Removing account with a partition should succeed" | |
| # Confirm previous operation succeeded | |
| output = atf.run_command_output( | |
| f"sacctmgr -n show assoc format=acct,user,lineage acct={acct4} partition={part1} cluster={cluster1}", | |
| fatal=True, | |
| ) | |
| assert ( | |
| output == "" | |
| ), f"Account {acct4} with partition {part1} shouldn't have an assoc anymore" | |
| @pytest.mark.xfail( | |
| atf.get_version("sbin/slurmdbd") < (25, 11, 6), | |
| reason="Ticket 24442: In 24.11.6+ we allow a user default account to be removed if more than one set", | |
| ) | |
| def test_add_del_user_def_acct(): | |
| """Test adding multiple default accounts""" | |
| # Add account | |
| atf.run_command( | |
| f"sacctmgr -i add acct {acct7} cluster={cluster1}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| # Add user with default account (no partition) | |
| atf.run_command( | |
| f"sacctmgr -i add user {user2} defaultaccount={acct7} cluster={cluster1}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| # Add user with default account and partition | |
| atf.run_command( | |
| f"sacctmgr -i add user {user2} defaultaccount={acct7} partition={part1} cluster={cluster1}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| # Confirm there are two defaults | |
| output = atf.run_command_output( | |
| f"sacctmgr -n -P show assoc where user={user2} format=acct onlydef", | |
| fatal=True, | |
| ) | |
| assert ( | |
| len(re.findall(rf"{acct7}", output, re.IGNORECASE)) == 2 | |
| ), f"User {user2} should have more than one default assoc" | |
| # Confirm the last default added should be able to be removed | |
| result = atf.run_command( | |
| f"sacctmgr -i del user {user2} acct={acct7} partition={part1} cluster={cluster1}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| assert ( | |
| result["exit_code"] == 0 | |
| ), f"Should be able to remove acct {acct7} for user {user2} with partition {part1}" | |
| # Add back in the default account (with partition) | |
| atf.run_command( | |
| f"sacctmgr -i add user {user2} defaultaccount={acct7} partition={part1} cluster={cluster1}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| # Confirm removing user assoc with default account AND no partition succeeds | |
| result = atf.run_command( | |
| f'sacctmgr -i del user {user2} acct={acct7} partition=\\"\\" cluster={cluster1}', | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| assert ( | |
| result["exit_code"] == 0 | |
| ), f"Should be able to remove acct {acct7} for user {user2} with empty partition" | |
| def test_change_user_def_acct(): | |
| """Test changing default account for user""" | |
| # Add two accts | |
| atf.run_command( | |
| f"sacctmgr -i add acct {acct8} cluster={cluster1}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| atf.run_command( | |
| f"sacctmgr -i add acct {acct9} cluster={cluster1}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| # Add user with default account and partition | |
| atf.run_command( | |
| f"sacctmgr -i add user {user3} defaultaccount={acct8} partition={part1} cluster={cluster1}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| # Add user with another default account and partition | |
| atf.run_command( | |
| f"sacctmgr -i add user {user3} defaultaccount={acct8} partition={part2} cluster={cluster1}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| # Add user with default account and no partition | |
| atf.run_command( | |
| f"sacctmgr -i add user {user3} defaultaccount={acct8} cluster={cluster1}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| # Add user with a non-def account and partition | |
| atf.run_command( | |
| f"sacctmgr -i add user {user3} account={acct9} partition={part3} cluster={cluster1}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| # Add user with a non-def account and another partition | |
| atf.run_command( | |
| f"sacctmgr -i add user {user3} account={acct9} partition={part4} cluster={cluster1}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| # Now change def account | |
| result = atf.run_command( | |
| f"sacctmgr -i mod user {user3} cluster={cluster1} set defaultaccount={acct9}", | |
| user=atf.properties["slurm-user"], | |
| fatal=True, | |
| ) | |
| assert ( | |
| result["exit_code"] == 0 | |
| ), f"Should be able to set new default account {acct9} for user {user3}" | |
| # Confirm total assocs for user | |
| output = atf.run_command_output( | |
| f"sacctmgr -n -P show assoc where user={user3} format=acct,partition", | |
| fatal=True, | |
| ) | |
| assert ( | |
| len(re.findall(rf"{acct8}", output, re.IGNORECASE)) == 3 | |
| ), f"User {user3} should have 3 assocs for acct {acct8}" | |
| assert ( | |
| len(re.findall(rf"{acct9}", output, re.IGNORECASE)) == 2 | |
| ), f"User {user3} should have 5 assocs for acct {acct9}" | |
| # Confirm there are two defaults with partitions | |
| output = atf.run_command_output( | |
| f"sacctmgr -n -P show assoc where user={user3} format=acct,partition onlydef", | |
| fatal=True, | |
| ) | |
| assert ( | |
| len(re.findall(rf"{acct9}\|part[0-9]", output, re.IGNORECASE)) == 2 | |
| ), f"User {user3} should have 2 default assocs with partitions" |