Skip to content

Commit

Permalink
ftest for daos pool list
Browse files Browse the repository at this point in the history
Skip-NLT: true
Skip-unit-test: true
Quick-functional: true
Test-tag: ListClientPoolsTest
Required-githooks: true
Change-Id: I829dd6e5d26ea8ff3f5c9ff0da758dcc92e90774
Signed-off-by: Michael MacDonald <[email protected]>
  • Loading branch information
mjmac committed Jun 28, 2024
1 parent d147fb3 commit 63aa2fe
Show file tree
Hide file tree
Showing 4 changed files with 275 additions and 5 deletions.
149 changes: 148 additions & 1 deletion src/tests/ftest/pool/list_pools.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
(C) Copyright 2018-2023 Intel Corporation.
(C) Copyright 2018-2024 Intel Corporation.
SPDX-License-Identifier: BSD-2-Clause-Patent
"""
Expand Down Expand Up @@ -126,3 +126,150 @@ def test_list_pools(self):
test_case, error))
if errors:
self.fail("Errors detected:\n {}".format("\n ".join(errors)))


class ListClientPoolsTest(TestWithServers):
"""Test class for daos pool list tests.
Test Class Description:
This class contains tests for client-side pool list.
:avocado: recursive
"""

def run_case(self, rank_lists, svcn=None):
"""Run test case.
Create pools, call daos pool list to get the list, and compare against
the UUIDs and service replicas returned at create time. The first pool
created will be marked inaccessible to clients, and should not show up
in the client list.
Args:
rank_lists (list): Rank lists. List of list of int.
svcn (str, optional): Service replicas. Defaults to None.
Raises:
CommandFailure: if there was an error destroying pools
TestFail: if there was an error verifying the created pools
"""
# Iterate rank lists to create pools. Store the created pool information
# as a dictionary of pool UUID keys with a service replica list value.
self.pool = []
expected_admin_uuids = {}
for rank_list in rank_lists:
self.pool.append(self.get_pool(target_list=rank_list, svcn=svcn))
expected_admin_uuids[self.pool[-1].uuid.lower()] = self.pool[-1].svc_ranks

# Remove the default ACLs that allow the creator to access the pool.
# These ACLs don't apply to dmg, but do apply to users.
offlimits = self.pool[0]
self.get_dmg_command().pool_delete_acl(offlimits.uuid, 'OWNER@')
self.get_dmg_command().pool_delete_acl(offlimits.uuid, 'GROUP@')
expected_user_uuids = expected_admin_uuids.copy()
del expected_user_uuids[offlimits.uuid.lower()]

# Verify the 'dmg pool list' command lists the correct created pool
# information.
detected_admin_uuids = {}
try:
for data in self.get_dmg_command().get_pool_list_all():
detected_admin_uuids[data["uuid"]] = data["svc_reps"]
except KeyError as error:
self.fail("Error parsing dmg pool list output: {}".format(error))

self.log.info("Expected admin pool info: %s", str(expected_admin_uuids))
self.log.info("Detected admin pool info: %s", str(detected_admin_uuids))

# Compare the expected and detected pool information
self.assertEqual(
expected_admin_uuids, detected_admin_uuids,
"dmg pool info does not list all expected pool UUIDs and their "
"service replicas")

# Verify the 'daos pool list' command lists the correct created pool
# information.
detected_user_uuids = {}
try:
for data in self.get_daos_command().get_pool_list_all():
detected_user_uuids[data["uuid"]] = data["svc_reps"]
except KeyError as error:
self.fail("Error parsing dmg pool list output: {}".format(error))

self.log.info("Expected user pool info: %s", str(expected_user_uuids))
self.log.info("Detected user pool info: %s", str(detected_user_uuids))

# Compare the expected and detected pool information
self.assertEqual(
expected_user_uuids, detected_user_uuids,
"daos pool info does not list all expected pool UUIDs and their "
"service replicas")

# Destroy all the pools
if self.destroy_pools(self.pool):
self.fail("Error destroying pools")
self.pool = []

def test_list_client_pools(self):
"""JIRA ID: DAOS-16038.
Test Description:
Create pools in different ranks, call daos pool list and verify the
output list matches the output returned when the pools were
created. The first pool created should be inaccessible to the client,
and should not be returned in the list.
:avocado: tags=all,full_regression
:avocado: tags=vm
:avocado: tags=pool
:avocado: tags=ListClientPoolsTest,test_list_client_pools
"""
ranks = list(range(len(self.hostlist_servers)))

# Create pools with different ranks:
# 1) Create 4 pools each using a different rank number
# 2) Create 2 pools using 2 ranks per pool
# 3) Create 4 pools each using the same 4 ranks for each pool
# 4) Create 3 pools using all ranks with --svcn=3
test_cases = [
(
"Create 4 pools each using a different rank number",
{"rank_lists": [[rank] for rank in ranks[:4]]}
),
(
"Create 2 pools using 2 ranks per pool",
{
"rank_lists":
[ranks[index:index + 2]
for index in range(0, len(ranks[:4]), 2)]
}
),
(
"Create 4 pools each using the same 4 ranks for each pool",
{"rank_lists": [ranks[:4] for _ in ranks[:4]]}
),
(
"Create 3 pools using all ranks with --nsvc=3",
{"rank_lists": [None for _ in ranks[:3]], "svcn": 3}
),
]
errors = []
for test_case, kwargs in test_cases:
self.log.info("%s", "-" * 80)
self.log.info("Running test case: %s", test_case)
self.log.info("%s", "-" * 80)
try:
self.run_case(**kwargs)

except TestFail as error:
message = "Error: {}: {}".format(test_case, error)
self.log.info(message)
errors.append(message)

except CommandFailure as error:
self.fail(
"Fatal test error detected during: {}: {}".format(
test_case, error))
if errors:
self.fail("Errors detected:\n {}".format("\n ".join(errors)))
6 changes: 5 additions & 1 deletion src/tests/ftest/util/command_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,11 @@ def set_command(self, sub_command_list=None, **kwargs):
if sub_command_list is not None:
for sub_command in sub_command_list:
full_command.set_sub_command(sub_command)
full_command = full_command.sub_command_class

if full_command.sub_command_class is not None:
full_command = full_command.sub_command_class
else:
raise CommandFailure(f"invalid sub command: {sub_command}")

# Update any argument values for the full command
full_command.update_params(**kwargs)
Expand Down
107 changes: 107 additions & 0 deletions src/tests/ftest/util/daos_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,113 @@ def pool_list_attrs(self, pool, sys_name=None, verbose=False):
("pool", "list-attrs"), pool=pool, sys_name=sys_name,
verbose=verbose)

def pool_list_containers(self, pool, sys_name=None):
"""List containers in the pool.
Args:
pool (str): pool label or UUID
sys_name (str): DAOS system name. Defaults to None.
Returns:
dict: JSON output
Raises:
CommandFailure: if the daos pool list-containers command fails.
"""
return self._get_json_result(
("pool", "list-containers"), pool=pool, sys_name=sys_name)

def pool_list(self, no_query=False, verbose=False):
"""List pools.
Args:
no_query (bool, optional): If True, do not query for pool stats.
verbose (bool, optional): If True, use verbose mode.
Raises:
CommandFailure: if the daos pool list command fails.
Returns:
dict: the daos json command output converted to a python dictionary
"""
# Sample verbose JSON Output:
# {
# "response": {
# "status": 0,
# "pools": [
# {
# "uuid": "517217db-47c4-4bb9-aae5-e38ca7b3dafc",
# "label": "mkp1",
# "svc_reps": [
# 0
# ],
# "total_targets": 8,
# "disabled_targets": 0,
# "usage": [
# {
# "tier_name": "SCM",
# "size": 3000000000,
# "free": 2995801112,
# "imbalance": 0
# },
# {
# "tier_name": "NVME",
# "size": 47000000000,
# "free": 26263322624,
# "imbalance": 36
# }
# ]
# }
# ]
# },
# "error": null,
# "status": 0
# }
return self._get_json_result(
("pool", "list"), no_query=no_query, verbose=verbose)

def _parse_pool_list(self, key=None, **kwargs):
"""Parse the daos pool list json output for the requested information.
Args:
key (str, optional): pool list json dictionary key in
["response"]["pools"]. Defaults to None.
Raises:
CommandFailure: if the daos pool list command fails.
Returns:
list: list of all the pool items in the daos pool list json output
for the requested json dictionary key. This will be an empty
list if the key does not exist or the json output was not in
the expected format.
"""
pool_list = self.pool_list(**kwargs)
try:
if pool_list["response"]["pools"] is None:
return []
if key:
return [pool[key] for pool in pool_list["response"]["pools"]]
return pool_list["response"]["pools"]
except KeyError:
return []

def get_pool_list_all(self, **kwargs):
"""Get a list of all the pool information from daos pool list.
Raises:
CommandFailure: if the daos pool list command fails.
Returns:
list: a list of dictionaries containing information for each pool
from the daos pool list json output
"""
return self._parse_pool_list(**kwargs)

def container_query(self, pool, cont, sys_name=None):
"""Query a container.
Expand Down
18 changes: 15 additions & 3 deletions src/tests/ftest/util/daos_utils_base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
(C) Copyright 2020-2023 Intel Corporation.
(C) Copyright 2020-2024 Intel Corporation.
SPDX-License-Identifier: BSD-2-Clause-Patent
"""
Expand Down Expand Up @@ -47,8 +47,10 @@ def __init__(self):

def get_sub_command_class(self):
# pylint: disable=redefined-variable-type
"""Get the dmg network sub command object."""
if self.sub_command.value in ("list-containers", "list"):
"""Get the daos pool sub command object."""
if self.sub_command.value == "list":
self.sub_command_class = self.ListSubCommand()
elif self.sub_command.value == "list-containers":
self.sub_command_class = self.ListContainersSubCommand()
elif self.sub_command.value == "query":
self.sub_command_class = self.QuerySubCommand()
Expand All @@ -65,6 +67,16 @@ def get_sub_command_class(self):
else:
self.sub_command_class = None

class ListSubCommand(CommandWithParameters):
"""Defines an object for the daos pool list command."""

def __init__(self):
"""Create a daos pool list command object."""
super().__init__("/run/daos/pool/list/*", "list")
self.sys_name = FormattedParameter("--sys-name={}")
self.no_query = FormattedParameter("--no-query", False)
self.verbose = FormattedParameter("--verbose", False)

class CommonPoolSubCommand(CommandWithParameters):
"""Defines an object for the common daos pool sub-command.
Expand Down

0 comments on commit 63aa2fe

Please sign in to comment.