Skip to content

Commit

Permalink
b/340244046 initial port collect command from master to 2.4
Browse files Browse the repository at this point in the history
Although this port copies the code from master to 2.4, there
are issues related to permissions making it non-functional.

This is a partial cherry-pick of the following PR's on master:
DAOS-10625 control: Create the tool to collect the logs/config for support purpose (#11094)
DAOS-13759 control: Update support collect-log tool. (#12906)
DAOS-13763 control: Fix daos_metrics collection for support collect-log. (#12555)
DAOS-13936 support: Collect the specific logs and Time range log for support (#13325)

Change-Id: I168c14e177a5003c4e315595b1bf154e84cef473
  • Loading branch information
cdavis28 committed May 20, 2024
1 parent 3b2748f commit 2913a20
Show file tree
Hide file tree
Showing 37 changed files with 4,141 additions and 112 deletions.
20 changes: 20 additions & 0 deletions src/control/cmd/daos_agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type cliOptions struct {
DumpInfo dumpAttachInfoCmd `command:"dump-attachinfo" description:"Dump system attachinfo"`
DumpTopo hwprov.DumpTopologyCmd `command:"dump-topology" description:"Dump system topology"`
NetScan netScanCmd `command:"net-scan" description:"Perform local network fabric scan"`
Support supportCmd `command:"support" description:"Perform debug tasks to help support team"`
}

type (
Expand Down Expand Up @@ -95,6 +96,25 @@ func exitWithError(log logging.Logger, err error) {
os.Exit(1)
}

type (
supportAgentConfig interface {
setSupportConf(string)
getSupportConf() string
}

supportAgentConfigCmd struct {
supportCfgPath string
}
)

func (cmd *supportAgentConfigCmd) setSupportConf(cfgPath string) {
cmd.supportCfgPath = cfgPath
}

func (cmd *supportAgentConfigCmd) getSupportConf() string {
return cmd.supportCfgPath
}

func parseOpts(args []string, opts *cliOptions, invoker control.Invoker, log *logging.LeveledLogger) error {
var wroteJSON atm.Bool
p := flags.NewParser(opts, flags.Default)
Expand Down
111 changes: 111 additions & 0 deletions src/control/cmd/daos_agent/support.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//
// (C) Copyright 2022-2023 Intel Corporation.
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//

package main

import (
"fmt"
"os"
"path/filepath"
"time"

"github.com/daos-stack/daos/src/control/common/cmdutil"
"github.com/daos-stack/daos/src/control/lib/support"
)

// supportCmd is the struct representing the top-level support subcommand.
type supportCmd struct {
CollectLog collectLogCmd `command:"collect-log" description:"Collect logs from client"`
agentConfigPath string
}

// collectLogCmd is the struct representing the command to collect the log from client side.
type collectLogCmd struct {
supportAgentConfigCmd
cmdutil.LogCmd
support.CollectLogSubCmd
}

func (cmd *collectLogCmd) Execute(_ []string) error {
err := cmd.DateTimeValidate()
if err != nil {
return err
}

var LogCollection = map[int32][]string{
support.CopyAgentConfigEnum: {""},
support.CollectAgentLogEnum: {""},
support.CollectAgentCmdEnum: support.AgentCmd,
support.CollectClientLogEnum: {""},
support.CollectSystemCmdEnum: support.SystemCmd,
}

// Default 3 steps of log/conf collection.
progress := support.ProgressBar{
Total: len(LogCollection),
NoDisplay: false,
}

if cmd.Archive {
progress.Total++
}

// Copy the custom log folder
if cmd.ExtraLogsDir != "" {
LogCollection[support.CollectExtraLogsDirEnum] = []string{""}
progress.Total++
}

if cmd.TargetFolder == "" {
folderName := fmt.Sprintf("daos_support_client_logs_%s", time.Now().Format(time.RFC3339))
cmd.TargetFolder = filepath.Join(os.TempDir(), folderName)
}

cmd.Infof("Support Logs will be copied to %s", cmd.TargetFolder)

progress.Steps = 100 / progress.Total
params := support.CollectLogsParams{}
params.TargetFolder = cmd.TargetFolder
params.ExtraLogsDir = cmd.ExtraLogsDir
params.Config = cmd.getSupportConf()
params.LogStartDate = cmd.LogStartDate
params.LogEndDate = cmd.LogEndDate
params.LogStartTime = cmd.LogStartTime
params.LogEndTime = cmd.LogEndTime
for logFunc, logCmdSet := range LogCollection {
for _, logCmd := range logCmdSet {
cmd.Debugf("Log Function Enum = %d -- Log Collect Cmd = %s ", logFunc, logCmd)
params.LogFunction = logFunc
params.LogCmd = logCmd

err := support.CollectSupportLog(cmd.Logger, params)
if err != nil {
fmt.Println(err)
if cmd.StopOnError {
return err
}
}
}
fmt.Printf(progress.Display())
}

if cmd.Archive {
cmd.Debugf("Archiving the Log Folder %s", cmd.TargetFolder)
err := support.ArchiveLogs(cmd.Logger, params)
if err != nil {
return err
}

// FIXME: DAOS-13290 Workaround for files held open
for i := 1; i < 3; i++ {
os.RemoveAll(cmd.TargetFolder)
}
}

fmt.Printf(progress.Display())

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

package main

import (
"bytes"
"encoding/json"
"io"
"os"
"strings"
"testing"

"github.com/google/go-cmp/cmp"

"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/logging"
)

type jsonCmdTest struct {
name string
cmd string
setHelpers func(opts *mainOpts)
expOut interface{} // JSON encoded data should output.
expErr error
}

func runJSONCmdTests(t *testing.T, log *logging.LeveledLogger, cmdTests []jsonCmdTest) {
t.Helper()

for _, tc := range cmdTests {
t.Run(tc.name, func(t *testing.T) {
t.Helper()

// Replace os.Stdout so that we can verify the generated output.
var result bytes.Buffer
r, w, _ := os.Pipe()
done := make(chan struct{})
go func() {
_, _ = io.Copy(&result, r)
close(done)
}()
stdout := os.Stdout
defer func() {
os.Stdout = stdout
}()
os.Stdout = w

var opts mainOpts
if tc.setHelpers != nil {
tc.setHelpers(&opts)
}
test.CmpErr(t, tc.expErr, parseOpts(strings.Split(tc.cmd, " "), &opts, log))

w.Close()
<-done

// Verify only JSON gets printed.
if !json.Valid(result.Bytes()) {
t.Fatalf("invalid JSON in response: %s", result.String())
}

var sb strings.Builder
if err := cmdutil.OutputJSON(&sb, tc.expOut, tc.expErr); err != nil {
if err != tc.expErr {
t.Fatalf("OutputJSON: %s", err)
}
}

if diff := cmp.Diff(sb.String(), result.String()); diff != "" {
t.Fatalf("unexpected stdout (-want, +got):\n%s\n", diff)
}
})
}
}
15 changes: 11 additions & 4 deletions src/control/cmd/daos_server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (

const defaultConfigFile = "daos_server.yml"

var errJSONOutputNotSupported = errors.New("this subcommand does not support JSON output")

type execTestFn func() error

type mainOpts struct {
Expand All @@ -51,6 +53,7 @@ type mainOpts struct {
MgmtSvc msCmdRoot `command:"ms" description:"Perform tasks related to management service replicas"`
DumpTopo hwprov.DumpTopologyCmd `command:"dump-topology" description:"Dump system topology"`
Config configCmd `command:"config" alias:"cfg" description:"Perform tasks related to configuration of hardware on the local server"`
Support supportCmd `command:"support" description:"Perform debug tasks to help support team"`

// Allow a set of tests to be run before executing commands.
preExecTests []execTestFn
Expand Down Expand Up @@ -92,10 +95,14 @@ func parseOpts(args []string, opts *mainOpts, log *logging.LeveledLogger) error
return errors.Errorf("unexpected commandline arguments: %v", cmdArgs)
}

if jsonCmd, ok := cmd.(cmdutil.JSONOutputter); ok && opts.JSON {
jsonCmd.EnableJSONOutput(os.Stdout, &wroteJSON)
// disable output on stdout other than JSON
log.ClearLevel(logging.LogLevelInfo)
if opts.JSON {
if jsonCmd, ok := cmd.(cmdutil.JSONOutputter); ok {
jsonCmd.EnableJSONOutput(os.Stdout, &wroteJSON)
// disable output on stdout other than JSON
log.ClearLevel(logging.LogLevelInfo)
} else {
return errJSONOutputNotSupported
}
}

switch cmd.(type) {
Expand Down
120 changes: 120 additions & 0 deletions src/control/cmd/daos_server/support.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//
// (C) Copyright 2022-2023 Intel Corporation.
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//

package main

import (
"fmt"
"os"
"path/filepath"
"time"

"github.com/daos-stack/daos/src/control/common/cmdutil"
"github.com/daos-stack/daos/src/control/lib/support"
)

// supportCmd is the struct representing the top-level support subcommand.
type supportCmd struct {
CollectLog collectLogCmd `command:"collect-log" description:"Collect logs from server"`
}

// collectLogCmd is the struct representing the command to collect the Logs/config for support purpose
type collectLogCmd struct {
cfgCmd
cmdutil.LogCmd
support.CollectLogSubCmd
support.LogTypeSubCmd
}

func (cmd *collectLogCmd) Execute(_ []string) error {
var LogCollection = map[int32][]string{}
err := cmd.DateTimeValidate()
if err != nil {
return err
}

// Only collect the specific logs Admin,Control or Engine.
// This will ignore the system information collection.
if cmd.LogType != "" {
LogCollection[support.CollectServerLogEnum], err = cmd.LogTypeValidate()
if err != nil {
return err
}
} else {
LogCollection[support.CopyServerConfigEnum] = []string{""}
LogCollection[support.CollectSystemCmdEnum] = support.SystemCmd
LogCollection[support.CollectDaosServerCmdEnum] = support.DaosServerCmd
LogCollection[support.CollectServerLogEnum], err = cmd.LogTypeValidate()
if err != nil {
return err
}
}

// Default 4 steps of log/conf collection.
progress := support.ProgressBar{
Total: len(LogCollection),
NoDisplay: false,
}

if cmd.Archive {
progress.Total++
}

// Copy custom log folder
if cmd.ExtraLogsDir != "" {
LogCollection[support.CollectExtraLogsDirEnum] = []string{""}
progress.Total++
}

if cmd.TargetFolder == "" {
folderName := fmt.Sprintf("daos_support_server_logs_%s", time.Now().Format(time.RFC3339))
cmd.TargetFolder = filepath.Join(os.TempDir(), folderName)
}
cmd.Infof("Support logs will be copied to %s", cmd.TargetFolder)

progress.Steps = 100 / progress.Total
params := support.CollectLogsParams{}
params.Config = cmd.configPath()
params.TargetFolder = cmd.TargetFolder
params.ExtraLogsDir = cmd.ExtraLogsDir
params.LogStartDate = cmd.LogStartDate
params.LogEndDate = cmd.LogEndDate
params.LogStartTime = cmd.LogStartTime
params.LogEndTime = cmd.LogEndTime
for logFunc, logCmdSet := range LogCollection {
for _, logCmd := range logCmdSet {
cmd.Debugf("Log Function Enum = %d -- Log Collect Cmd = %s ", logFunc, logCmd)
params.LogFunction = logFunc
params.LogCmd = logCmd

err := support.CollectSupportLog(cmd.Logger, params)
if err != nil {
fmt.Println(err)
if cmd.StopOnError {
return err
}
}
}
fmt.Printf(progress.Display())
}

if cmd.Archive {
cmd.Debugf("Archiving the Log Folder %s", cmd.TargetFolder)
err := support.ArchiveLogs(cmd.Logger, params)
if err != nil {
return err
}

// FIXME: DAOS-13290 Workaround for files held open
for i := 1; i < 3; i++ {
os.RemoveAll(cmd.TargetFolder)
}
}

fmt.Printf(progress.Display())

return nil
}
Loading

0 comments on commit 2913a20

Please sign in to comment.