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-16304 tools: Add daos health net-test command #14980

Merged
merged 4 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 7 additions & 3 deletions src/control/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ def install_go_bin(env, name, libs=None, install_man=False):
build_path = join('$BUILD_DIR/src/control', f'{name}.8')
menv = env.Clone()
# This runs code from the build area so needs LD_LIBRARY_PATH set.
menv.d_enable_ld_path(["cart", "gurt", "client/api", "common", "client/dfs", "utils"])
menv.d_enable_ld_path(["cart", "gurt", "client/api", "common", "client/dfs", "utils",
"utils/self_test"])
menv.Command(build_path, target, f'{gen_bin} manpage -o {build_path}')
menv.Install('$PREFIX/share/man/man8', build_path)

Expand Down Expand Up @@ -151,9 +152,12 @@ def scons():
"-L$BUILD_DIR/src/cart "
"-L$BUILD_DIR/src/common "
"-L$BUILD_DIR/src/client/dfs "
"-L$BUILD_DIR/src/utils $_RPATH")
"-L$BUILD_DIR/src/utils "
"-L$BUILD_DIR/src/utils/self_test "
"$_RPATH")
dbenv.AppendENVPath("CGO_LDFLAGS", dblibs, sep=" ")
install_go_bin(dbenv, 'daos', libs=['daos_cmd_hdlrs', 'dfs', 'duns', 'daos'],
install_go_bin(dbenv, 'daos', libs=['daos_cmd_hdlrs', 'dfs', 'duns', 'daos',
'daos_self_test'],
install_man=True)

if not prereqs.server_requested():
Expand Down
71 changes: 69 additions & 2 deletions src/control/cmd/daos/health.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ import (

"github.com/daos-stack/daos/src/control/build"
"github.com/daos-stack/daos/src/control/cmd/daos/pretty"
"github.com/daos-stack/daos/src/control/common/cmdutil"
"github.com/daos-stack/daos/src/control/lib/daos"
"github.com/daos-stack/daos/src/control/lib/ranklist"
"github.com/daos-stack/daos/src/control/lib/ui"
"github.com/daos-stack/daos/src/control/logging"
)

type healthCmds struct {
Check healthCheckCmd `command:"check" description:"Perform DAOS system health checks"`
Check healthCheckCmd `command:"check" description:"Perform DAOS system health checks"`
NetTest netTestCmd `command:"net-test" description:"Perform non-destructive DAOS networking tests"`

Choose a reason for hiding this comment

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

Is "non-destructive" necessary here? we don't qualify the above health check command at all

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 wanted to convey to the user that the test won't overwrite any data, even though it's doing writes. Simpler to just write "non-destructive" to allay any concerns, otherwise the concerned user has to go read documentation to figure out whether or not they should be worried.

}

type healthCheckCmd struct {
Expand Down Expand Up @@ -68,7 +72,7 @@ func (cmd *healthCheckCmd) Execute([]string) error {
return err
}

sysInfo, err := cmd.apiProvider.GetSystemInfo()
sysInfo, err := cmd.apiProvider.GetSystemInfo(cmd.MustLogCtx())

Choose a reason for hiding this comment

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

Curious why this change? seems a bit cumbersome to pass a thing from the same object (cmd) into a submethos onto itself. Should we consider adding a method to cmd instead? cmd.GetSysInfor() to take care of all other call sites?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As discussed in another conversation with @kjacque, I'm experimenting with a couple of different approaches to implementing the bindings, hence the provider thing here. This change specifically was just to establish a precedent that the API calls should have a context.

As for why we're getting a context from the command object and giving it to the API call, that's a whole other conversation that boils down to patterns encouraged by the use of go-flags, which is what we settled on years ago as the CLI framework for these commands.

if err != nil {
cmd.Errorf("failed to query system information: %v", err)
}
Expand Down Expand Up @@ -166,3 +170,66 @@ func (cmd *healthCheckCmd) Execute([]string) error {

return nil
}

type netTestCmd struct {
cmdutil.JSONOutputCmd
cmdutil.LogCmd
sysCmd
Ranks ui.RankSetFlag `short:"r" long:"ranks" description:"Use the specified ranks as test endpoints (default: all)"`
Tags ui.RankSetFlag `short:"t" long:"tags" description:"Use the specified tags on ranks" default:"0"`
XferSize ui.ByteSizeFlag `short:"s" long:"size" description:"Per-RPC transfer size (send/reply)"`
MaxInflight uint `short:"m" long:"max-inflight" description:"Maximum number of inflight RPCs"`
RepCount uint `short:"c" long:"rep-count" description:"Number of times to repeat the RPCs, per endpoint"`
TpsBytes bool `short:"y" long:"bytes" description:"Show throughput values in bytes per second"`
Verbose bool `short:"v" long:"verbose" description:"Display more detailed DAOS network testing information"`
}

func (cmd *netTestCmd) Execute(_ []string) error {
cfg := &daos.SelfTestConfig{
GroupName: cmd.SysName,
EndpointRanks: cmd.Ranks.Ranks(),
EndpointTags: ranklist.RanksToUint32(cmd.Tags.Ranks()),
MaxInflightRPCs: cmd.MaxInflight,
Repetitions: cmd.RepCount,
}
if cmd.XferSize.IsSet() {
// If set, use that size, otherwise use the zero value.
cfg.SendSizes = []uint64{cmd.XferSize.Bytes}
cfg.ReplySizes = cfg.SendSizes
}
if err := cfg.SetDefaults(); err != nil {
return err
}

if !cmd.JSONOutputEnabled() {
var cfgBuf strings.Builder
if err := pretty.PrintSelfTestConfig(&cfgBuf, cfg, cmd.Verbose); err != nil {
return err
}
cmd.Info(cfgBuf.String())
cmd.Info("Starting non-destructive network test (duration depends on performance)...\n\n")
knard-intel marked this conversation as resolved.
Show resolved Hide resolved
}

res, err := RunSelfTest(cmd.MustLogCtx(), cfg)
if err != nil {
return err
}

if cmd.JSONOutputEnabled() {
return cmd.OutputJSON(struct {
Cfg *daos.SelfTestConfig `json:"configuration"`
Res []*daos.SelfTestResult `json:"results"`
}{
Cfg: cfg,
Res: res,
}, nil)
}

var resBuf strings.Builder
if err := pretty.PrintSelfTestResults(&resBuf, res, cmd.Verbose, cmd.TpsBytes); err != nil {
return err
}
cmd.Info(resBuf.String())

return nil
}
64 changes: 64 additions & 0 deletions src/control/cmd/daos/health_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// (C) Copyright 2024 Intel Corporation.
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//

package main

import (
"context"
"testing"

"github.com/dustin/go-humanize"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"

"github.com/daos-stack/daos/src/control/common/cmdutil"
"github.com/daos-stack/daos/src/control/common/test"
"github.com/daos-stack/daos/src/control/lib/daos"
"github.com/daos-stack/daos/src/control/lib/ranklist"
"github.com/daos-stack/daos/src/control/lib/ui"
"github.com/daos-stack/daos/src/control/logging"
)

func RunSelfTest(ctx context.Context, cfg *daos.SelfTestConfig) ([]*daos.SelfTestResult, error) {
return []*daos.SelfTestResult{}, nil
}

func TestDaos_netTestCmdExecute(t *testing.T) {
// Quickie smoke test for the UI -- will flesh out later.
var opts cliOptions
log, buf := logging.NewTestLogger(t.Name())
defer test.ShowBufferOnFailure(t, buf)
args := []string{
"health", "net-test",
"--ranks", "0-3",
"--tags", "4-9",
"--size", "20 MiB",
"--rep-count", "2222",
"--bytes", "--verbose",
}
expArgs := netTestCmd{}
expArgs.Ranks.Replace(ranklist.MustCreateRankSet("0-3"))
expArgs.Tags.Replace(ranklist.MustCreateRankSet("4-9"))
expArgs.XferSize.Bytes = 20 * humanize.MiByte
expArgs.RepCount = 2222
expArgs.Verbose = true
expArgs.TpsBytes = true

if err := parseOpts(args, &opts, log); err != nil {
t.Fatal(err)
}
cmpOpts := cmp.Options{
cmpopts.IgnoreUnexported(netTestCmd{}),
cmp.Comparer(func(a, b ranklist.RankSet) bool {
return a.String() == b.String()
}),
cmp.Comparer(func(a, b ui.ByteSizeFlag) bool {
return a.String() == b.String()
}),
cmpopts.IgnoreTypes(cmdutil.LogCmd{}, cmdutil.JSONOutputCmd{}),
}
test.CmpAny(t, "health net-test args", expArgs, opts.Health.NetTest, cmpOpts...)
}
3 changes: 2 additions & 1 deletion src/control/cmd/daos/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/daos-stack/daos/src/control/common/cmdutil"
"github.com/daos-stack/daos/src/control/fault"
"github.com/daos-stack/daos/src/control/lib/atm"
"github.com/daos-stack/daos/src/control/lib/daos"
"github.com/daos-stack/daos/src/control/logging"
)

Expand Down Expand Up @@ -182,7 +183,7 @@ or query/manage an object inside a container.`
// Initialize the daos debug system first so that

Choose a reason for hiding this comment

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

Is this comment still accurate with change below?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, because initializing logging also initializes debug, and there's no way to initialize debug without initializing logging.

// any allocations made as part of argument parsing
// are logged when running under NLT.
debugFini, err := initDaosDebug()
debugFini, err := daos.InitLogging(daos.UnsetLogMask)
if err != nil {
exitWithError(log, err)
}
Expand Down
Loading
Loading