Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DAOS-14698 crtl: Create container with attributes #15300

Open
wants to merge 27 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5fcd159
DAOS-14698 crtl: Create container with attributes
kanard38 Oct 9, 2024
315f440
DAOS-14698 crtl: Create container with attributes
kanard38 Oct 9, 2024
865f2e5
DAOS-14698 crtl: Create container with attributes
kanard38 Oct 9, 2024
4dd51bd
Merge remote-tracking branch 'origin/master' into ckochhof/dev/master…
kanard38 Oct 22, 2024
819b856
DAOS-14698 crtl: Create container with attributes
kanard38 Oct 9, 2024
b922f70
DAOS-14698 crtl: Create container with attributes
kanard38 Oct 9, 2024
825512d
Merge remote-tracking branch 'origin/master' into ckochhof/dev/master…
kanard38 Oct 23, 2024
2e9afbd
DAOS-14698 crtl: Create container with attributes
kanard38 Oct 9, 2024
2d26f4d
Merge remote-tracking branch 'origin/master' into ckochhof/dev/master…
kanard38 Oct 23, 2024
055c3a0
DAOS-14698 crtl: Create container with attributes
kanard38 Oct 9, 2024
a3d0a02
Merge remote-tracking branch 'origin/master' into ckochhof/dev/master…
kanard38 Oct 24, 2024
7f26733
DAOS-14698 crtl: Create container with attributes
kanard38 Oct 9, 2024
5e72550
Merge remote-tracking branch 'origin/master' into ckochhof/dev/master…
kanard38 Oct 25, 2024
5dca3b4
Merge remote-tracking branch 'origin/master' into ckochhof/dev/master…
kanard38 Oct 25, 2024
98bd3c2
Merge remote-tracking branch 'origin/master' into ckochhof/dev/master…
kanard38 Oct 28, 2024
a390e8a
DAOS-14698 crtl: Create container with attributes
kanard38 Oct 9, 2024
9af9320
Merge remote-tracking branch 'origin/master' into ckochhof/dev/master…
kanard38 Oct 29, 2024
d2ee639
DAOS-14698 crtl: Create container with attributes
kanard38 Oct 9, 2024
90186bc
Merge remote-tracking branch 'origin/master' into ckochhof/dev/master…
kanard38 Nov 4, 2024
0561a5b
DAOS-14698 crtl: Create container with attributes
kanard38 Oct 9, 2024
263168f
DAOS-14698 crtl: Create container with attributes
kanard38 Oct 9, 2024
436436f
DAOS-14698 crtl: Create container with attributes
kanard38 Oct 9, 2024
f30184d
Merge remote-tracking branch 'origin/master' into ckochhof/dev/master…
kanard38 Nov 5, 2024
b1aaa8f
Merge remote-tracking branch 'origin/master' into ckochhof/dev/master…
kanard38 Nov 15, 2024
af54e76
DAOS-14698 crtl: Create container with attributes
kanard38 Nov 5, 2024
d230c87
DAOS-14698 crtl: Create container with attributes
kanard38 Nov 5, 2024
e3f510d
DAOS-14698 crtl: Create container with attributes
kanard38 Nov 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 42 additions & 3 deletions src/client/dfs/duns.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* (C) Copyright 2019-2023 Intel Corporation.
* (C) Copyright 2019-2024 Intel Corporation.
*
* SPDX-License-Identifier: BSD-2-Clause-Patent
*/
Expand Down Expand Up @@ -899,8 +899,9 @@ duns_link_lustre_path(const char *pool, const char *cont, daos_cont_layout_t typ
}
#endif

int
duns_create_path(daos_handle_t poh, const char *path, struct duns_attr_t *attrp)
static int
create_path(daos_handle_t poh, const char *path, int count, char const *const names[],
void const *const values[], size_t const sizes[], struct duns_attr_t *attrp)
{
char type[10];
daos_pool_info_t info = {0};
Expand Down Expand Up @@ -1080,6 +1081,31 @@ duns_create_path(daos_handle_t poh, const char *path, struct duns_attr_t *attrp)
D_GOTO(err_link, rc);
}

if (count > 0) {
daos_handle_t coh;

D_ASSERT(names != NULL);
D_ASSERT(values != NULL);
D_ASSERT(sizes != NULL);

rc = daos_cont_open(poh, attrp->da_cont, DAOS_COO_RW, &coh, NULL, NULL);
if (rc != 0) {
DL_ERROR(rc, "Failed to open container %s", attrp->da_cont);
D_GOTO(err_cont, rc = daos_der2errno(rc));
}
rc = daos_cont_set_attr(coh, count, names, values, sizes, NULL);
rc2 = daos_cont_close(coh, NULL);
if (rc != 0) {
DL_ERROR(rc, "Failed to set user attribute of container %s",
attrp->da_cont);
D_GOTO(err_cont, rc = daos_der2errno(rc));
}
if (rc2 != 0) {
DL_ERROR(rc2, "failed to close container %s", attrp->da_cont);
D_GOTO(err_cont, rc = daos_der2errno(rc2));
}
}

/** store the daos attributes in the path xattr */
len =
snprintf(str, DUNS_MAX_XATTR_LEN, DUNS_XATTR_FMT, type, attrp->da_pool, attrp->da_cont);
Expand Down Expand Up @@ -1128,6 +1154,19 @@ duns_create_path(daos_handle_t poh, const char *path, struct duns_attr_t *attrp)
return rc;
}

int
duns_create_path(daos_handle_t poh, const char *path, struct duns_attr_t *attrp)
{
return create_path(poh, path, 0, NULL, NULL, NULL, attrp);
}

int
duns_create_path_attr(daos_handle_t poh, const char *path, int count, char const *const names[],
void const *const values[], size_t const sizes[], struct duns_attr_t *attrp)
{
return create_path(poh, path, count, names, values, sizes, attrp);
}

int
duns_link_cont(daos_handle_t poh, const char *cont, const char *path)
{
Expand Down
48 changes: 47 additions & 1 deletion src/control/cmd/daos/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ type containerCreateCmd struct {
Mode ConsModeFlag `long:"mode" short:"M" description:"DFS consistency mode"`
ACLFile string `long:"acl-file" short:"A" description:"input file containing ACL"`
Group ui.ACLPrincipalFlag `long:"group" short:"g" description:"group who will own the container (group@[domain])"`
Attrs ui.SetPropertiesFlag `long:"attrs" short:"a" description:"user-defined attributes (key:val[,key:val...])"`
Args struct {
Label string `positional-arg-name:"label"`
} `positional-args:"yes"`
Expand Down Expand Up @@ -405,6 +406,25 @@ func (cmd *containerCreateCmd) contCreate() (string, error) {
contID = cmd.contUUID.String()
}

if len(cmd.Attrs.ParsedProps) != 0 {
kjacque marked this conversation as resolved.
Show resolved Hide resolved
attrs := make(attrList, 0, len(cmd.Attrs.ParsedProps))
for key, val := range cmd.Attrs.ParsedProps {
attrs = append(attrs, &attribute{
Name: key,
Value: []byte(val),
})
}

if err := cmd.openContainer(C.DAOS_COO_RW); err != nil {
return "", errors.Wrapf(err, "failed to open new container %s", contID)
}
defer cmd.closeContainer()

if err := setDaosAttributes(cmd.cContHandle, contAttr, attrs); err != nil {
return "", errors.Wrapf(err, "failed to set user attributes on new container %s", contID)
}
}

cmd.Infof("Successfully created container %s", contID)
return contID, nil
}
Expand Down Expand Up @@ -456,7 +476,33 @@ func (cmd *containerCreateCmd) contCreateUNS() (string, error) {
cPath := C.CString(cmd.Path)
defer freeString(cPath)

dunsErrno := C.duns_create_path(cmd.cPoolHandle, cPath, &dattr)
var dunsErrno C.int
attrs := cmd.Attrs.ParsedProps
if len(attrs) == 0 {
dunsErrno = C.duns_create_path(cmd.cPoolHandle, cPath, &dattr)
} else {
i := 0
attrNames := make([]*C.char, len(attrs))
valSizes := make([]C.size_t, len(attrs))
valBufs := make([]unsafe.Pointer, len(attrs))
for key, val := range attrs {
attrNames[i] = C.CString(key)
valSizes[i] = C.size_t(len(val))
valBufs[i] = C.malloc(valSizes[i])
valSlice := (*[1 << 30]byte)(valBufs[i])
copy(valSlice[:], val)
i++
}
defer func(nameSlice []*C.char, bufSlice []unsafe.Pointer) {
for i := 0; i < len(nameSlice); i++ {
freeString(nameSlice[i])
C.free(bufSlice[i])
}
}(attrNames, valBufs)

dunsErrno = C.duns_create_path_attr(cmd.cPoolHandle, cPath, C.int(len(attrs)), &attrNames[0], &valBufs[0], &valSizes[0], &dattr)
}

rc := C.daos_errno2der(dunsErrno)
if err := daosError(rc); err != nil {
return "", errors.Wrapf(err, "duns_create_path() failed")
Expand Down
28 changes: 28 additions & 0 deletions src/include/daos_uns.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,34 @@ struct duns_attr_t {
int
duns_create_path(daos_handle_t poh, const char *path, struct duns_attr_t *attrp);

/**
* Create a special directory (POSIX) or file (HDF5) depending on the container type, and create a
* new DAOS container with a givren set of user attributes. The uuid of the container can be either
* passed in \a attrp->da_cuuid (deprecated) or generated internally and returned in \a da_cont.
* The extended attributes are set on the dir/file created that points to pool uuid, container
* uuid. This is to be used in a unified namespace solution to be able to map a path in the unified
* namespace to a location in the DAOS tier. The container and pool can have labels, but the UNS
* stores the uuids only and so labels are ignored in \a attrp.
* The user is not required to call duns_destory_attrs on \a attrp as this call does not allocate
* any buffers in attrp.
*
* \param[in] poh Pool handle
* \param[in] path Valid path in an existing namespace.
* \param[in] count Number of container attributes
* \param[in] names Array of \a count null-terminated container attribute names.
* \param[in] values Array of \a count container attribute values
* \param[in] sizes Array of \a count elements containing the sizes of respective container
* attribute values.
* \param[in,out]
* attrp Struct containing the attributes. The uuid of the
* container created is returned in da_cuuid.
*
* \return 0 on Success. errno code on failure.
*/
int
duns_create_path_attr(daos_handle_t poh, const char *path, int count, char const *const names[],
void const *const values[], size_t const sizes[], struct duns_attr_t *attrp);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

im not a fan of this new API. it might be useful to have something for the command line.
but for the API, it's not a big deal to do
daos_cont_create()
daos_cont_set_attr()
I would suggest to remove it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure to fully understand your comments.
Are you suggesting to remove duns_create_path_attr() and duns_create_path() functions or just the first one.
If we are just removing duns_create_path_attr(), then we will have two solutions from my understanding:

  1. Change the dfuse code to poll the attributes of its mounted containers.
  2. split the function duns_create_path_attr() to not create and bind the container in the same function. By this way, we will be able to use the function daos_cont_set_attr() between the two function calls.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no you cannot remove duns_create_path()!
my question is why you are adding this new API (duns_crreate_path_attr()) ?
what is the challenge of doing:
duns_create_path()
daos_cont_set_attr()
?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my understanding, doing this will not work as the attributes are cached inside DFuse after the binding.
Thus, when we will get back from duns_create_path() , the call to daos_cont_set_attr() will not be taken into account by DFuse.
@ashleypittman could you confirm this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've taken a closer look and think you're both right, having a new API doesn't seem like the right solution here, however I hadn't realised it was the same C function that both created the container and inserted it into the POSIX namespace.

The best path might be:
daos_cont_create()
daos_cont_set_attr()
duns_link_cont()

Copy link
Contributor Author

@knard-intel knard-intel Oct 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was not aware of the duns_link_cont() function, which for sure is a better solution.
Thanks @ashleypittman for the hints and @mchaarawi for the relevant remark :-)

  • Remove useless new function duns_create_path_attr()
  • Split sub container creation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was not aware of the duns_link_cont() function, which for sure is a better solution. Thanks @ashleypittman for the hints and @mchaarawi for the relevant remark :-)

  • Remove useless new function duns_create_path_attr()
  • Split sub container creation

Fixed with commit 819b856


/**
* Retrieve the pool and container uuids from a path corresponding to a DAOS location. If this was a
* path created with duns_create_path(), then this call would return the pool, container, and type
Expand Down
113 changes: 113 additions & 0 deletions src/tests/ftest/dfuse/container_attrs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
"""
(C) Copyright 2020-2024 Intel Corporation.

SPDX-License-Identifier: BSD-2-Clause-Patent
"""

import os
import re

from apricot import TestWithServers
from dfuse_utils import get_dfuse, start_dfuse


class DfuseContainerAttrs(TestWithServers):
"""Check if the dfuse attributes of a container are properly managed.

:avocado: recursive
"""

def setUp(self):
"""Set up each test case."""
# obtain separate logs
self.update_log_file_names()

# Start the servers and agents
super().setUp()

def _check_attrs(self, dfuse, container_name):
phender marked this conversation as resolved.
Show resolved Hide resolved
"""Check if the DFuse attributes of a container are loaded

Check in the log file of the dfuse instance if it contains the DFuse attributes of a given
container. It also checks the value of the attributes found.

Args:
dfuse (Dfuse): DFuse instance to check
container_name (str): Name of the container
"""
log_file = dfuse.get_log_file()
attrs = self.params.get("attrs", f"/run/{container_name}/*")
for attr in [attr.split(':') for attr in attrs.split(",")]:
match = None
attr_re = re.compile(r"^.+\ssetting\s+'" + attr[0] + r"'\s+is\s+(\d+)\s+seconds$")
for line in log_file:
match = attr_re.match(line)
phender marked this conversation as resolved.
Show resolved Hide resolved
if match:
self.assertEqual(
attr[1],
match.group(1),
f"Unexpected value for attribute {attr[0]}: "
f"want={attr[1]}, got={match.group(1)}")
break
self.assertIsNotNone(match, f"Setting of attribute {attr[0]} not found")

def test_dfuse_container_attrs(self):
"""Jira ID: DAOS-14698.

Test Description:
Create a container with DFuse attributes
Mount a DFuse mount point
Check the output of the DFuse log
ashleypittman marked this conversation as resolved.
Show resolved Hide resolved
:avocado: tags=all,pr
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking - do these tests really need to be pr? Or could they be daily_regression or full_regression?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no opinion on this, and do not fill legitimate to have one.
I will let you decide with the other test members.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need your input :) Roughly, it's something like

  • Is the test critical and quick?
    • yes -> pr
    • no -> Is the test fairly important or very quick?
      • yes -> daily_regression
      • no -> weekly_regression

IMO these new tests belong in daily_regression unless they are very critical to base funcionality. @ashleypittman thoughts?

Related, I think we should add the daos_cmd feature tag to these tests since they verify some GO code paths for the daos command

Copy link
Contributor Author

@knard-intel knard-intel Oct 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Add daos_cmd feature tag
  • Change tags from pr to daily_regression

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Add daos_cmd feature tag
  • Change tags from pr to daily_regression

Fixed with commit 7f26733

:avocado: tags=vm
:avocado: tags=dfuse,container
:avocado: tags=DfuseContainerAttrs,test_dfuse_container_attrs
"""
self.log.info("Creating DAOS pool")
pool = self.get_pool()

self.log_step("Creating DAOS container with Dfuse attributes")
container = self.get_container(pool, namespace="/run/container_01/*")

self.log_step("Mounting DFuse mount point")
dfuse = get_dfuse(self, self.hostlist_clients)
dfuse.env["D_LOG_FLUSH"] = "INFO"
start_dfuse(self, dfuse, pool, container)

self.log_step("Checking DFuse log file")
self._check_attrs(dfuse, "container_01")

self.log_step("Test passed")

def test_dfuse_subcontainer_attrs(self):
"""Jira ID: DAOS-14698.

Test Description:
Create a container
Mount a DFuse mount point
Create a sub-container with DFuse attributes
Check the output of the DFuse log
:avocado: tags=all,pr
:avocado: tags=vm
:avocado: tags=dfuse,container
:avocado: tags=DfuseContainerAttrs,test_dfuse_subcontainer_attrs
"""
self.log.info("Creating DAOS pool")
pool = self.get_pool()

self.log_step("Creating DAOS container")
container = self.get_container(pool, namespace="/run/container_02/*")

self.log_step("Mounting DFuse mount point")
dfuse = get_dfuse(self, self.hostlist_clients)
dfuse.env["D_LOG_FLUSH"] = "INFO"
start_dfuse(self, dfuse, pool, container)

self.log_step("Creating DAOS subcontainer with DFuse attributes")
sub_dir = os.path.join(dfuse.mount_dir.value, "foo")
container = self.get_container(pool, namespace="/run/container_03/*", path=sub_dir)

self.log_step("Checking DFuse log file")
self._check_attrs(dfuse, "container_03")

self.log_step("Test passed")
30 changes: 30 additions & 0 deletions src/tests/ftest/dfuse/container_attrs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
hosts:
test_servers: 1
test_clients: 1

timeout: 200

server_config:
name: daos_server
engines_per_host: 1
engines:
0:
log_file: daos_server0.log
storage: auto

pool:
scm_size: 80%

container_01:
type: POSIX
attrs: dfuse-attr-time:666,dfuse-dentry-time:999
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. This will be useful for some other tests as well. Once this lands I'll write up a ticket to identify those (for the test team to handle)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it would be an idea to have a way of exporting these from dfuse somehow. For now having a python function in ftest would be useful and then if we have a more direct way of querying it then we can plug it in later.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant it's nice to specify attrs on create, not necessarily dfuse though.
If dfuse already has the attrs what would container create need them for?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see what you mean. My mistake.

control_method: daos

container_02:
type: POSIX
control_method: daos

container_03:
type: POSIX
attrs: dfuse-attr-time:42,dfuse-dentry-time:404
control_method: daos
8 changes: 6 additions & 2 deletions src/tests/ftest/util/daos_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def pool_autotest(self, pool):

def container_create(self, pool, sys_name=None, path=None, cont_type=None,
oclass=None, dir_oclass=None, file_oclass=None, chunk_size=None,
properties=None, acl_file=None, label=None):
properties=None, acl_file=None, label=None, attrs=None):
# pylint: disable=too-many-arguments
"""Create a container.

Expand All @@ -96,6 +96,8 @@ def container_create(self, pool, sys_name=None, path=None, cont_type=None,
pairs defining the container properties. Defaults to None
acl_file (str, optional): ACL file. Defaults to None.
label (str, optional): Container label. Defaults to None.
attrs (str, optional): String of comma-separated <name>:<value> pairs defining the
container user attributes. Defaults to None.

Returns:
dict: the daos json command output converted to a python dictionary
Expand All @@ -110,10 +112,12 @@ def container_create(self, pool, sys_name=None, path=None, cont_type=None,
properties += ',rd_lvl:1'
else:
properties = 'rd_lvl:1'

return self._get_json_result(
("container", "create"), pool=pool, sys_name=sys_name, path=path,
type=cont_type, oclass=oclass, dir_oclass=dir_oclass, file_oclass=file_oclass,
chunk_size=chunk_size, properties=properties, acl_file=acl_file, label=label)
chunk_size=chunk_size, properties=properties, acl_file=acl_file, label=label,
attrs=attrs)

def container_clone(self, src, dst):
"""Clone a container to a new container.
Expand Down
3 changes: 3 additions & 0 deletions src/tests/ftest/util/daos_utils_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,9 @@ def __init__(self):
# --acl-file=PATH
# input file containing ACL
self.acl_file = FormattedParameter("--acl-file={}", None)
# --attrs=<name>:<value>[,<name>:<value>,...]
# user-defined attributes
self.attrs = FormattedParameter("--attrs={}", None)

class CreateSnapSubCommand(CommonContainerSubCommand):
"""Defines an object for the daos container create-snap command."""
Expand Down
Loading
Loading