diff --git a/src/control/cmd/ddb/commands_wrapper.go b/src/control/cmd/ddb/commands_wrapper.go index eaa26832c09..6035fbaafb8 100644 --- a/src/control/cmd/ddb/commands_wrapper.go +++ b/src/control/cmd/ddb/commands_wrapper.go @@ -14,12 +14,10 @@ import ( "unsafe" "github.com/daos-stack/daos/src/control/lib/daos" - "github.com/daos-stack/daos/src/control/logging" ) /* #cgo CFLAGS: -I${SRCDIR}/../../../utils/ddb/ - #cgo LDFLAGS: -lddb -lgurt #include #include @@ -37,46 +35,39 @@ func freeString(s *C.char) { C.free(unsafe.Pointer(s)) } -func SetCString(out **C.char, s string) func() { - cstr := C.CString(s) - *out = cstr - - return func() { - C.free(unsafe.Pointer(cstr)) - } +// DdbContext wraps the C ddb_ctx structure and provides Go methods for all ddb operations. +type DdbContext struct { + ctx C.struct_ddb_ctx } -// InitDdb initializes the ddb context and returns a closure to finalize it. -func InitDdb(log *logging.LeveledLogger) (*DdbContext, func(), error) { +// Init initializes the ddb context and returns a cleanup function that must be +// called when done. +func (ctx *DdbContext) Init() (func(), error) { // Must lock to OS thread because vos init/fini uses ABT init and finalize which must be called on the same thread runtime.LockOSThread() - if err := daosError(C.ddb_init()); err != nil { + if err := daosError(ddb_init()); err != nil { runtime.UnlockOSThread() - return nil, nil, err + return nil, err } - ctx := &DdbContext{} - C.ddb_ctx_init(&ctx.ctx) // Initialize with ctx default values - ctx.log = log + ddb_ctx_init(&ctx.ctx) // Initialize with ctx default values - return ctx, func() { - C.ddb_fini() + return func() { + ddb_fini() runtime.UnlockOSThread() }, nil } -// DdbContext structure for wrapping the C code context structure -type DdbContext struct { - ctx C.struct_ddb_ctx - log *logging.LeveledLogger -} - -func ddbPoolIsOpen(ctx *DdbContext) bool { - return bool(C.ddb_pool_is_open(&ctx.ctx)) +// PoolIsOpen reports whether a VOS pool is currently open in the context. +func (ctx *DdbContext) PoolIsOpen() bool { + return bool(ddb_pool_is_open(&ctx.ctx)) } -func ddbLs(ctx *DdbContext, path string, recursive bool, details bool) error { +// Ls lists the contents at the given VOS tree path. +// When recursive is true, subdirectories are traversed. +// When details is true, additional metadata is printed for each entry. +func (ctx *DdbContext) Ls(path string, recursive bool, details bool) error { /* Set up the options */ options := C.struct_ls_options{} options.path = C.CString(path) @@ -84,36 +75,44 @@ func ddbLs(ctx *DdbContext, path string, recursive bool, details bool) error { options.recursive = C.bool(recursive) options.details = C.bool(details) /* Run the c code command */ - return daosError(C.ddb_run_ls(&ctx.ctx, &options)) + return daosError(ddb_run_ls(&ctx.ctx, &options)) } -func ddbOpen(ctx *DdbContext, path string, write_mode bool) error { +// Open opens the VOS pool file at path using the DAOS management database at dbPath. +// When writeMode is true, the pool is opened for writing. +func (ctx *DdbContext) Open(path string, dbPath string, writeMode bool) error { /* Set up the options */ options := C.struct_open_options{} options.path = C.CString(path) defer freeString(options.path) - options.db_path = ctx.ctx.dc_db_path - options.write_mode = C.bool(write_mode) + options.db_path = C.CString(dbPath) + defer freeString(options.db_path) + options.write_mode = C.bool(writeMode) /* Run the c code command */ - return daosError(C.ddb_run_open(&ctx.ctx, &options)) + return daosError(ddb_run_open(&ctx.ctx, &options)) } -func ddbVersion(ctx *DdbContext) error { +// Version prints the ddb tool version information. +func (ctx *DdbContext) Version() error { /* Run the c code command */ - return daosError(C.ddb_run_version(&ctx.ctx)) + return daosError(ddb_run_version(&ctx.ctx)) } -func ddbClose(ctx *DdbContext) error { +// Close closes the currently opened VOS pool. +func (ctx *DdbContext) Close() error { /* Run the c code command */ - return daosError(C.ddb_run_close(&ctx.ctx)) + return daosError(ddb_run_close(&ctx.ctx)) } -func ddbSuperblockDump(ctx *DdbContext) error { +// SuperblockDump prints the VOS pool superblock information. +func (ctx *DdbContext) SuperblockDump() error { /* Run the c code command */ - return daosError(C.ddb_run_superblock_dump(&ctx.ctx)) + return daosError(ddb_run_superblock_dump(&ctx.ctx)) } -func ddbValueDump(ctx *DdbContext, path string, dst string) error { +// ValueDump dumps the value at the given VOS tree path. +// If dst is non-empty, the value is written to that file instead of stdout. +func (ctx *DdbContext) ValueDump(path string, dst string) error { /* Set up the options */ options := C.struct_value_dump_options{} options.path = C.CString(path) @@ -121,19 +120,21 @@ func ddbValueDump(ctx *DdbContext, path string, dst string) error { options.dst = C.CString(dst) defer freeString(options.dst) /* Run the c code command */ - return daosError(C.ddb_run_value_dump(&ctx.ctx, &options)) + return daosError(ddb_run_value_dump(&ctx.ctx, &options)) } -func ddbRm(ctx *DdbContext, path string) error { +// Rm removes the VOS tree node at the given path. +func (ctx *DdbContext) Rm(path string) error { /* Set up the options */ options := C.struct_rm_options{} options.path = C.CString(path) defer freeString(options.path) /* Run the c code command */ - return daosError(C.ddb_run_rm(&ctx.ctx, &options)) + return daosError(ddb_run_rm(&ctx.ctx, &options)) } -func ddbValueLoad(ctx *DdbContext, src string, dst string) error { +// ValueLoad loads a value from the file at src into the VOS tree at dst. +func (ctx *DdbContext) ValueLoad(src string, dst string) error { /* Set up the options */ options := C.struct_value_load_options{} options.src = C.CString(src) @@ -141,37 +142,42 @@ func ddbValueLoad(ctx *DdbContext, src string, dst string) error { options.dst = C.CString(dst) defer freeString(options.dst) /* Run the c code command */ - return daosError(C.ddb_run_value_load(&ctx.ctx, &options)) + return daosError(ddb_run_value_load(&ctx.ctx, &options)) } -func ddbIlogDump(ctx *DdbContext, path string) error { +// IlogDump prints the incarnation log entries at the given VOS tree path. +func (ctx *DdbContext) IlogDump(path string) error { /* Set up the options */ options := C.struct_ilog_dump_options{} options.path = C.CString(path) defer freeString(options.path) /* Run the c code command */ - return daosError(C.ddb_run_ilog_dump(&ctx.ctx, &options)) + return daosError(ddb_run_ilog_dump(&ctx.ctx, &options)) } -func ddbIlogCommit(ctx *DdbContext, path string) error { +// IlogCommit commits the pending incarnation log entries at the given VOS tree path. +func (ctx *DdbContext) IlogCommit(path string) error { /* Set up the options */ options := C.struct_ilog_commit_options{} options.path = C.CString(path) defer freeString(options.path) /* Run the c code command */ - return daosError(C.ddb_run_ilog_commit(&ctx.ctx, &options)) + return daosError(ddb_run_ilog_commit(&ctx.ctx, &options)) } -func ddbIlogClear(ctx *DdbContext, path string) error { +// IlogClear clears the incarnation log entries at the given VOS tree path. +func (ctx *DdbContext) IlogClear(path string) error { /* Set up the options */ options := C.struct_ilog_clear_options{} options.path = C.CString(path) defer freeString(options.path) /* Run the c code command */ - return daosError(C.ddb_run_ilog_clear(&ctx.ctx, &options)) + return daosError(ddb_run_ilog_clear(&ctx.ctx, &options)) } -func ddbDtxDump(ctx *DdbContext, path string, active bool, committed bool) error { +// DtxDump prints DTX records at the given VOS tree path. +// Pass active or committed to filter the output to the respective DTX table. +func (ctx *DdbContext) DtxDump(path string, active bool, committed bool) error { /* Set up the options */ options := C.struct_dtx_dump_options{} options.path = C.CString(path) @@ -179,77 +185,87 @@ func ddbDtxDump(ctx *DdbContext, path string, active bool, committed bool) error options.active = C.bool(active) options.committed = C.bool(committed) /* Run the c code command */ - return daosError(C.ddb_run_dtx_dump(&ctx.ctx, &options)) + return daosError(ddb_run_dtx_dump(&ctx.ctx, &options)) } -func ddbDtxCmtClear(ctx *DdbContext, path string) error { +// DtxCmtClear clears committed DTX entries at the given VOS tree path. +func (ctx *DdbContext) DtxCmtClear(path string) error { /* Set up the options */ options := C.struct_dtx_cmt_clear_options{} options.path = C.CString(path) defer freeString(options.path) /* Run the c code command */ - return daosError(C.ddb_run_dtx_cmt_clear(&ctx.ctx, &options)) + return daosError(ddb_run_dtx_cmt_clear(&ctx.ctx, &options)) } -func ddbSmdSync(ctx *DdbContext, nvme_conf string, db_path string) error { +// SmdSync synchronizes the SMD database with the NVMe configuration in nvmeConf, +// using the DAOS management database at dbPath. +func (ctx *DdbContext) SmdSync(nvmeConf string, dbPath string) error { /* Set up the options */ options := C.struct_smd_sync_options{} - options.nvme_conf = C.CString(nvme_conf) + options.nvme_conf = C.CString(nvmeConf) defer freeString(options.nvme_conf) - options.db_path = C.CString(db_path) + options.db_path = C.CString(dbPath) defer freeString(options.db_path) /* Run the c code command */ - return daosError(C.ddb_run_smd_sync(&ctx.ctx, &options)) + return daosError(ddb_run_smd_sync(&ctx.ctx, &options)) } -func ddbVeaDump(ctx *DdbContext) error { +// VeaDump prints the VEA (Versioned Extent Allocator) free-extent information. +func (ctx *DdbContext) VeaDump() error { /* Run the c code command */ - return daosError(C.ddb_run_vea_dump(&ctx.ctx)) + return daosError(ddb_run_vea_dump(&ctx.ctx)) } -func ddbVeaUpdate(ctx *DdbContext, offset string, blk_cnt string) error { +// VeaUpdate updates the VEA free-extent entry at the given offset with blkCnt blocks. +func (ctx *DdbContext) VeaUpdate(offset string, blkCnt string) error { /* Set up the options */ options := C.struct_vea_update_options{} options.offset = C.CString(offset) defer freeString(options.offset) - options.blk_cnt = C.CString(blk_cnt) + options.blk_cnt = C.CString(blkCnt) defer freeString(options.blk_cnt) /* Run the c code command */ - return daosError(C.ddb_run_vea_update(&ctx.ctx, &options)) + return daosError(ddb_run_vea_update(&ctx.ctx, &options)) } -func ddbDtxActCommit(ctx *DdbContext, path string, dtx_id string) error { +// DtxActCommit force-commits the active DTX entry identified by dtxID at the given path. +func (ctx *DdbContext) DtxActCommit(path string, dtxID string) error { /* Set up the options */ options := C.struct_dtx_act_options{} options.path = C.CString(path) defer freeString(options.path) - options.dtx_id = C.CString(dtx_id) + options.dtx_id = C.CString(dtxID) defer freeString(options.dtx_id) /* Run the c code command */ - return daosError(C.ddb_run_dtx_act_commit(&ctx.ctx, &options)) + return daosError(ddb_run_dtx_act_commit(&ctx.ctx, &options)) } -func ddbDtxActAbort(ctx *DdbContext, path string, dtx_id string) error { +// DtxActAbort force-aborts the active DTX entry identified by dtxID at the given path. +func (ctx *DdbContext) DtxActAbort(path string, dtxID string) error { /* Set up the options */ options := C.struct_dtx_act_options{} options.path = C.CString(path) defer freeString(options.path) - options.dtx_id = C.CString(dtx_id) + options.dtx_id = C.CString(dtxID) defer freeString(options.dtx_id) /* Run the c code command */ - return daosError(C.ddb_run_dtx_act_abort(&ctx.ctx, &options)) + return daosError(ddb_run_dtx_act_abort(&ctx.ctx, &options)) } -func ddbFeature(ctx *DdbContext, path, enable, disable string, show bool) error { +// Feature manages VOS pool compat and incompat feature flags at the given path. +// Pass enable or disable to set or clear the named flags; pass show to list current flags. +func (ctx *DdbContext) Feature(path, dbPath, enable, disable string, show bool) error { /* Set up the options */ options := C.struct_feature_options{} options.path = C.CString(path) defer freeString(options.path) - options.db_path = ctx.ctx.dc_db_path + options.db_path = C.CString(dbPath) + defer freeString(options.db_path) if enable != "" { cEnable := C.CString(enable) defer freeString(cEnable) - err := daosError(C.ddb_feature_string2flags(&ctx.ctx, cEnable, + err := daosError(ddb_feature_string2flags(&ctx.ctx, cEnable, &options.set_compat_flags, &options.set_incompat_flags)) if err != nil { return err @@ -258,7 +274,7 @@ func ddbFeature(ctx *DdbContext, path, enable, disable string, show bool) error if disable != "" { cDisable := C.CString(disable) defer freeString(cDisable) - err := daosError(C.ddb_feature_string2flags(&ctx.ctx, cDisable, + err := daosError(ddb_feature_string2flags(&ctx.ctx, cDisable, &options.clear_compat_flags, &options.clear_incompat_flags)) if err != nil { return err @@ -266,98 +282,101 @@ func ddbFeature(ctx *DdbContext, path, enable, disable string, show bool) error } options.show_features = C.bool(show) /* Run the c code command */ - return daosError(C.ddb_run_feature(&ctx.ctx, &options)) + return daosError(ddb_run_feature(&ctx.ctx, &options)) } -func ddbRmPool(ctx *DdbContext, path string) error { +// RmPool removes the VOS pool file at path, using the DAOS management database at dbPath. +func (ctx *DdbContext) RmPool(path string, dbPath string) error { /* Set up the options */ options := C.struct_rm_pool_options{} options.path = C.CString(path) defer freeString(options.path) - options.db_path = ctx.ctx.dc_db_path + options.db_path = C.CString(dbPath) + defer freeString(options.db_path) /* Run the c code command */ - return daosError(C.ddb_run_rm_pool(&ctx.ctx, &options)) + return daosError(ddb_run_rm_pool(&ctx.ctx, &options)) } -func ddbDtxActDiscardInvalid(ctx *DdbContext, path string, dtx_id string) error { +// DtxActDiscardInvalid discards the invalid active DTX entry identified by dtxID at the given path. +func (ctx *DdbContext) DtxActDiscardInvalid(path string, dtxID string) error { /* Set up the options */ options := C.struct_dtx_act_options{} options.path = C.CString(path) defer freeString(options.path) - options.dtx_id = C.CString(dtx_id) + options.dtx_id = C.CString(dtxID) defer freeString(options.dtx_id) /* Run the c code command */ - return daosError(C.ddb_run_dtx_act_discard_invalid(&ctx.ctx, &options)) + return daosError(ddb_run_dtx_act_discard_invalid(&ctx.ctx, &options)) } -func ddbDevList(ctx *DdbContext, db_path string) error { +// DevList lists the SMD devices recorded in the DAOS management database at dbPath. +func (ctx *DdbContext) DevList(dbPath string) error { /* Set up the options */ options := C.struct_dev_list_options{} - options.db_path = C.CString(db_path) + options.db_path = C.CString(dbPath) defer freeString(options.db_path) /* Run the c code command */ - return daosError(C.ddb_run_dev_list(&ctx.ctx, &options)) + return daosError(ddb_run_dev_list(&ctx.ctx, &options)) } -func ddbDevReplace(ctx *DdbContext, db_path string, old_devid string, new_devid string) error { +// DevReplace replaces the device with oldDevID by newDevID in the SMD database at dbPath. +func (ctx *DdbContext) DevReplace(dbPath string, oldDevID string, newDevID string) error { /* Set up the options */ options := C.struct_dev_replace_options{} - options.db_path = C.CString(db_path) + options.db_path = C.CString(dbPath) defer freeString(options.db_path) - options.old_devid = C.CString(old_devid) + options.old_devid = C.CString(oldDevID) defer freeString(options.old_devid) - options.new_devid = C.CString(new_devid) + options.new_devid = C.CString(newDevID) defer freeString(options.new_devid) /* Run the c code command */ - return daosError(C.ddb_run_dev_replace(&ctx.ctx, &options)) + return daosError(ddb_run_dev_replace(&ctx.ctx, &options)) } -func ddbDtxStat(ctx *DdbContext, path string, details bool) error { +// DtxStat prints DTX statistics at the given VOS tree path. +// When details is true, per-entry statistics are also printed. +func (ctx *DdbContext) DtxStat(path string, details bool) error { /* Set up the options */ options := C.struct_dtx_stat_options{} options.path = C.CString(path) defer freeString(options.path) options.details = C.bool(details) /* Run the c code command */ - return daosError(C.ddb_run_dtx_stat(&ctx.ctx, &options)) + return daosError(ddb_run_dtx_stat(&ctx.ctx, &options)) } -func ddbProvMem(ctx *DdbContext, db_path string, tmpfs_mount string, tmpfs_mount_size uint) error { +// ProvMem provisions MD-on-SSD memory for the pool at dbPath. +// It mounts a tmpfs at tmpfsMount with size tmpfsMountSize bytes. +func (ctx *DdbContext) ProvMem(dbPath string, tmpfsMount string, tmpfsMountSize uint) error { /* Set up the options */ options := C.struct_prov_mem_options{} - options.db_path = C.CString(db_path) + options.db_path = C.CString(dbPath) defer freeString(options.db_path) - options.tmpfs_mount = C.CString(tmpfs_mount) + options.tmpfs_mount = C.CString(tmpfsMount) defer freeString(options.tmpfs_mount) - options.tmpfs_mount_size = C.uint(tmpfs_mount_size) + options.tmpfs_mount_size = C.uint(tmpfsMountSize) /* Run the c code command */ - return daosError(C.ddb_run_prov_mem(&ctx.ctx, &options)) + return daosError(ddb_run_prov_mem(&ctx.ctx, &options)) } -func ddbDtxAggr(ctx *DdbContext, path string, cmt_time uint64, cmt_date string) error { - if cmt_time != math.MaxUint64 && cmt_date != "" { - ctx.log.Error("'--cmt_time' and '--cmt_date' options are mutually exclusive") - return daosError(-C.DER_INVAL) - } - if cmt_time == math.MaxUint64 && cmt_date == "" { - ctx.log.Error("'--cmt_time' or '--cmt_date' option has to be defined") - return daosError(-C.DER_INVAL) - } - +// DtxAggr runs DTX aggregation on the VOS tree at path. +// Exactly one of cmtTime (a POSIX timestamp) or cmtDate (a date string) must be specified; +// aggregation discards DTX entries committed before that point in time. +func (ctx *DdbContext) DtxAggr(path string, cmtTime uint64, cmtDate string) error { /* Set up the options */ options := C.struct_dtx_aggr_options{} options.path = C.CString(path) defer freeString(options.path) - if cmt_time != math.MaxUint64 { + if cmtTime != math.MaxUint64 { options.format = C.DDB_DTX_AGGR_CMT_TIME - options.cmt_time = C.uint64_t(cmt_time) + options.cmt_time = C.uint64_t(cmtTime) } - if cmt_date != "" { + if cmtDate != "" { options.format = C.DDB_DTX_AGGR_CMT_DATE - options.cmt_date = C.CString(cmt_date) + options.cmt_date = C.CString(cmtDate) defer freeString(options.cmt_date) } /* Run the c code command */ - return daosError(C.ddb_run_dtx_aggr(&ctx.ctx, &options)) + return daosError(ddb_run_dtx_aggr(&ctx.ctx, &options)) } diff --git a/src/control/cmd/ddb/ddb_commands.go b/src/control/cmd/ddb/ddb_commands.go index d515499264c..aa7f245be9e 100644 --- a/src/control/cmd/ddb/ddb_commands.go +++ b/src/control/cmd/ddb/ddb_commands.go @@ -9,6 +9,7 @@ package main import ( + "fmt" "math" "github.com/desertbit/grumble" @@ -30,7 +31,7 @@ func addAppCommands(app *grumble.App, ctx *DdbContext) { a.String("path", "Optional, list contents of the provided path", grumble.Default("")) }, Run: func(c *grumble.Context) error { - return ddbLs(ctx, c.Args.String("path"), c.Flags.Bool("recursive"), c.Flags.Bool("details")) + return ctx.Ls(c.Args.String("path"), c.Flags.Bool("recursive"), c.Flags.Bool("details")) }, Completer: nil, }) @@ -38,24 +39,20 @@ func addAppCommands(app *grumble.App, ctx *DdbContext) { app.AddCommand(&grumble.Command{ Name: "open", Aliases: nil, - Help: "Opens the vos file at ", - LongHelp: `Opens the vos file at . The '-w' option allows for modifying the vos file + Help: "Opens the VOS file at ", + LongHelp: `Opens the VOS file at . The '-w' option allows for modifying the VOS file with the rm, load, commit_ilog, etc commands. The path should be an absolute path to the pool shard. Part of the path is used to determine what the pool uuid is.`, HelpGroup: "vos", Flags: func(f *grumble.Flags) { - f.Bool("w", "write_mode", false, "Open the vos file in write mode.") + f.Bool("w", "write_mode", false, "Open the VOS file in write mode.") f.String("p", "db_path", "", "Path to the sys db to open.") }, Args: func(a *grumble.Args) { - a.String("path", "Path to the vos file to open.") + a.String("path", "Path to the VOS file to open.") }, Run: func(c *grumble.Context) error { - if c.Flags.String("db_path") != "" { - cleanup := SetCString(&ctx.ctx.dc_db_path, c.Flags.String("db_path")) - defer cleanup() - } - return ddbOpen(ctx, c.Args.String("path"), c.Flags.Bool("write_mode")) + return ctx.Open(c.Args.String("path"), c.Flags.String("db_path"), c.Flags.Bool("write_mode")) }, Completer: openCompleter, }) @@ -67,7 +64,7 @@ pool shard. Part of the path is used to determine what the pool uuid is.`, LongHelp: "", HelpGroup: "", Run: func(c *grumble.Context) error { - return ddbVersion(ctx) + return ctx.Version() }, Completer: nil, }) @@ -75,11 +72,11 @@ pool shard. Part of the path is used to determine what the pool uuid is.`, app.AddCommand(&grumble.Command{ Name: "close", Aliases: nil, - Help: "Close the currently opened vos pool shard", + Help: "Close the currently opened VOS file", LongHelp: "", HelpGroup: "vos", Run: func(c *grumble.Context) error { - return ddbClose(ctx) + return ctx.Close() }, Completer: nil, }) @@ -91,7 +88,7 @@ pool shard. Part of the path is used to determine what the pool uuid is.`, LongHelp: "", HelpGroup: "vos", Run: func(c *grumble.Context) error { - return ddbSuperblockDump(ctx) + return ctx.SuperblockDump() }, Completer: nil, }) @@ -100,7 +97,7 @@ pool shard. Part of the path is used to determine what the pool uuid is.`, Name: "value_dump", Aliases: nil, Help: "Dump a value", - LongHelp: `Dump a value to the screen or file. The vos path should be a complete + LongHelp: `Dump a value to the screen or file. The VOS path should be a complete path, including the akey and if the value is an array value it should include the extent. If a path to a file was provided then the value will be written to the file, else it will be printed to the screen.`, @@ -110,7 +107,7 @@ the file, else it will be printed to the screen.`, a.String("dst", "File path to dump the value to.", grumble.Default("")) }, Run: func(c *grumble.Context) error { - return ddbValueDump(ctx, c.Args.String("path"), c.Args.String("dst")) + return ctx.ValueDump(c.Args.String("path"), c.Args.String("dst")) }, Completer: nil, }) @@ -126,7 +123,7 @@ and everything under it, to a single value.`, a.String("path", "VOS tree path to remove.") }, Run: func(c *grumble.Context) error { - return ddbRm(ctx, c.Args.String("path")) + return ctx.Rm(c.Args.String("path")) }, Completer: nil, }) @@ -134,20 +131,20 @@ and everything under it, to a single value.`, app.AddCommand(&grumble.Command{ Name: "value_load", Aliases: nil, - Help: "Load a value to a vos path. ", - LongHelp: `Load a value to a vos path. This can be used to update + Help: "Load a value to a VOS path. ", + LongHelp: `Load a value to a VOS path. This can be used to update the value of an existing key, or create a new key. The is a path to a -file on the file system. The is a vos tree path to a value where the +file on the file system. The is a VOS tree path to a value where the data will be loaded. If the path currently exists, then the destination path must match the value type, meaning, if the value type is an array, then the path must include the extent, otherwise, it must not.`, HelpGroup: "vos", Args: func(a *grumble.Args) { a.String("src", "Source file path.") - a.String("dst", "Destination vos tree path to a value.") + a.String("dst", "Destination VOS tree path to a value.") }, Run: func(c *grumble.Context) error { - return ddbValueLoad(ctx, c.Args.String("src"), c.Args.String("dst")) + return ctx.ValueLoad(c.Args.String("src"), c.Args.String("dst")) }, Completer: nil, }) @@ -162,7 +159,7 @@ the path must include the extent, otherwise, it must not.`, a.String("path", "VOS tree path to an object, dkey, or akey.") }, Run: func(c *grumble.Context) error { - return ddbIlogDump(ctx, c.Args.String("path")) + return ctx.IlogDump(c.Args.String("path")) }, Completer: nil, }) @@ -177,7 +174,7 @@ the path must include the extent, otherwise, it must not.`, a.String("path", "VOS tree path to an object, dkey, or akey.") }, Run: func(c *grumble.Context) error { - return ddbIlogCommit(ctx, c.Args.String("path")) + return ctx.IlogCommit(c.Args.String("path")) }, Completer: nil, }) @@ -192,7 +189,7 @@ the path must include the extent, otherwise, it must not.`, a.String("path", "VOS tree path to an object, dkey, or akey.") }, Run: func(c *grumble.Context) error { - return ddbIlogClear(ctx, c.Args.String("path")) + return ctx.IlogClear(c.Args.String("path")) }, Completer: nil, }) @@ -211,7 +208,7 @@ the path must include the extent, otherwise, it must not.`, a.String("path", "VOS tree path to a container.") }, Run: func(c *grumble.Context) error { - return ddbDtxDump(ctx, c.Args.String("path"), c.Flags.Bool("active"), c.Flags.Bool("committed")) + return ctx.DtxDump(c.Args.String("path"), c.Flags.Bool("active"), c.Flags.Bool("committed")) }, Completer: nil, }) @@ -226,7 +223,7 @@ the path must include the extent, otherwise, it must not.`, a.String("path", "VOS tree path to a container.") }, Run: func(c *grumble.Context) error { - return ddbDtxCmtClear(ctx, c.Args.String("path")) + return ctx.DtxCmtClear(c.Args.String("path")) }, Completer: nil, }) @@ -239,10 +236,10 @@ the path must include the extent, otherwise, it must not.`, HelpGroup: "smd", Args: func(a *grumble.Args) { a.String("nvme_conf", "Path to the nvme conf file. (default /mnt/daos/daos_nvme.conf)", grumble.Default("")) - a.String("db_path", "Path to the vos db. (default /mnt/daos)", grumble.Default("")) + a.String("db_path", "Path to the sys db. (default /mnt/daos)", grumble.Default("")) }, Run: func(c *grumble.Context) error { - return ddbSmdSync(ctx, c.Args.String("nvme_conf"), c.Args.String("db_path")) + return ctx.SmdSync(c.Args.String("nvme_conf"), c.Args.String("db_path")) }, Completer: nil, }) @@ -254,7 +251,7 @@ the path must include the extent, otherwise, it must not.`, LongHelp: "", HelpGroup: "vos", Run: func(c *grumble.Context) error { - return ddbVeaDump(ctx) + return ctx.VeaDump() }, Completer: nil, }) @@ -270,7 +267,7 @@ the path must include the extent, otherwise, it must not.`, a.String("blk_cnt", "Total blocks of the region to mark free.") }, Run: func(c *grumble.Context) error { - return ddbVeaUpdate(ctx, c.Args.String("offset"), c.Args.String("blk_cnt")) + return ctx.VeaUpdate(c.Args.String("offset"), c.Args.String("blk_cnt")) }, Completer: nil, }) @@ -286,7 +283,7 @@ the path must include the extent, otherwise, it must not.`, a.String("dtx_id", "DTX id of the entry to commit. ") }, Run: func(c *grumble.Context) error { - return ddbDtxActCommit(ctx, c.Args.String("path"), c.Args.String("dtx_id")) + return ctx.DtxActCommit(c.Args.String("path"), c.Args.String("dtx_id")) }, Completer: nil, }) @@ -302,7 +299,7 @@ the path must include the extent, otherwise, it must not.`, a.String("dtx_id", "DTX id of the entry to abort. ") }, Run: func(c *grumble.Context) error { - return ddbDtxActAbort(ctx, c.Args.String("path"), c.Args.String("dtx_id")) + return ctx.DtxActAbort(c.Args.String("path"), c.Args.String("dtx_id")) }, Completer: nil, }) @@ -310,20 +307,20 @@ the path must include the extent, otherwise, it must not.`, app.AddCommand(&grumble.Command{ Name: "feature", Aliases: nil, - Help: "Manage vos pool features", + Help: "Manage VOS pool features", LongHelp: "", HelpGroup: "vos", Flags: func(f *grumble.Flags) { - f.String("e", "enable", "", "Enable vos pool features") - f.String("d", "disable", "", "Disable vos pool features") + f.String("e", "enable", "", "Enable VOS pool features") + f.String("d", "disable", "", "Disable VOS pool features") f.String("p", "db_path", "", "Path to the sys db") f.Bool("s", "show", false, "Show current features") }, Args: func(a *grumble.Args) { - a.String("path", "Optional, Path to the vos file", grumble.Default("")) + a.String("path", "Optional, Path to the VOS file", grumble.Default("")) }, Run: func(c *grumble.Context) error { - return ddbFeature(ctx, c.Args.String("path"), c.Flags.String("enable"), c.Flags.String("disable"), c.Flags.Bool("show")) + return ctx.Feature(c.Args.String("path"), c.Flags.String("db_path"), c.Flags.String("enable"), c.Flags.String("disable"), c.Flags.Bool("show")) }, Completer: featureCompleter, }) @@ -331,17 +328,17 @@ the path must include the extent, otherwise, it must not.`, app.AddCommand(&grumble.Command{ Name: "rm_pool", Aliases: nil, - Help: "Remove a vos pool.", + Help: "Remove a VOS pool file.", LongHelp: "", HelpGroup: "vos", Flags: func(f *grumble.Flags) { - f.String("p", "db_path", "", "Path to the sys db.") + f.String("p", "db_path", "", "Path to the sys db") }, Args: func(a *grumble.Args) { - a.String("path", "Optional, Path to the vos file", grumble.Default("")) + a.String("path", "Path to the VOS file") }, Run: func(c *grumble.Context) error { - return ddbRmPool(ctx, c.Args.String("path")) + return ctx.RmPool(c.Args.String("path"), c.Flags.String("db_path")) }, Completer: rmPoolCompleter, }) @@ -357,7 +354,7 @@ the path must include the extent, otherwise, it must not.`, a.String("dtx_id", "DTX id of the entry to validate or 'all' to validate all active DTX entries.") }, Run: func(c *grumble.Context) error { - return ddbDtxActDiscardInvalid(ctx, c.Args.String("path"), c.Args.String("dtx_id")) + return ctx.DtxActDiscardInvalid(c.Args.String("path"), c.Args.String("dtx_id")) }, Completer: nil, }) @@ -369,10 +366,10 @@ the path must include the extent, otherwise, it must not.`, LongHelp: "", HelpGroup: "vos", Args: func(a *grumble.Args) { - a.String("db_path", "Path to the vos db.") + a.String("db_path", "Path to the sys db.") }, Run: func(c *grumble.Context) error { - return ddbDevList(ctx, c.Args.String("db_path")) + return ctx.DevList(c.Args.String("db_path")) }, Completer: nil, }) @@ -384,12 +381,12 @@ the path must include the extent, otherwise, it must not.`, LongHelp: "", HelpGroup: "vos", Args: func(a *grumble.Args) { - a.String("db_path", "Path to the vos db.") + a.String("db_path", "Path to the sys db.") a.String("old_dev", "Old device UUID.") a.String("new_dev", "New device UUID.") }, Run: func(c *grumble.Context) error { - return ddbDevReplace(ctx, c.Args.String("db_path"), c.Args.String("old_dev"), c.Args.String("new_dev")) + return ctx.DevReplace(c.Args.String("db_path"), c.Args.String("old_dev"), c.Args.String("new_dev")) }, Completer: nil, }) @@ -407,7 +404,7 @@ the path must include the extent, otherwise, it must not.`, a.String("path", "Optional, VOS tree path of a container to query.", grumble.Default("")) }, Run: func(c *grumble.Context) error { - return ddbDtxStat(ctx, c.Args.String("path"), c.Flags.Bool("details")) + return ctx.DtxStat(c.Args.String("path"), c.Flags.Bool("details")) }, Completer: nil, }) @@ -426,7 +423,7 @@ the path must include the extent, otherwise, it must not.`, a.String("tmpfs_mount", "Path to the tmpfs mountpoint.") }, Run: func(c *grumble.Context) error { - return ddbProvMem(ctx, c.Args.String("db_path"), c.Args.String("tmpfs_mount"), c.Flags.Uint("tmpfs_size")) + return ctx.ProvMem(c.Args.String("db_path"), c.Args.String("tmpfs_mount"), c.Flags.Uint("tmpfs_size")) }, Completer: nil, }) @@ -445,7 +442,15 @@ the path must include the extent, otherwise, it must not.`, f.String("d", "cmt_date", "", "Max aggregation committed date (format '1970-01-01 00:00:00')") }, Run: func(c *grumble.Context) error { - return ddbDtxAggr(ctx, c.Args.String("path"), c.Flags.Uint64("cmt_time"), c.Flags.String("cmt_date")) + cmtTime := c.Flags.Uint64("cmt_time") + cmtDate := c.Flags.String("cmt_date") + if cmtTime != math.MaxUint64 && cmtDate != "" { + return fmt.Errorf("'--cmt_time' and '--cmt_date' options are mutually exclusive") + } + if cmtTime == math.MaxUint64 && cmtDate == "" { + return fmt.Errorf("'--cmt_time' or '--cmt_date' option has to be defined") + } + return ctx.DtxAggr(c.Args.String("path"), cmtTime, cmtDate) }, Completer: nil, }) diff --git a/src/control/cmd/ddb/libddb.go b/src/control/cmd/ddb/libddb.go new file mode 100644 index 00000000000..a0dd34ffa79 --- /dev/null +++ b/src/control/cmd/ddb/libddb.go @@ -0,0 +1,133 @@ +// +// (C) Copyright 2026 Hewlett Packard Enterprise Development LP. +// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// + +//go:build !test_stubs + +package main + +/* + #cgo CFLAGS: -I${SRCDIR}/../../../utils/ddb/ + #cgo LDFLAGS: -lddb -lgurt + + #include +*/ +import "C" + +func ddb_init() C.int { return C.ddb_init() } +func ddb_fini() { C.ddb_fini() } +func ddb_ctx_init(ctx *C.struct_ddb_ctx) { C.ddb_ctx_init(ctx) } + +func ddb_pool_is_open(ctx *C.struct_ddb_ctx) C.bool { + return C.ddb_pool_is_open(ctx) +} + +func ddb_run_ls(ctx *C.struct_ddb_ctx, opts *C.struct_ls_options) C.int { + return C.ddb_run_ls(ctx, opts) +} + +func ddb_run_open(ctx *C.struct_ddb_ctx, opts *C.struct_open_options) C.int { + return C.ddb_run_open(ctx, opts) +} + +func ddb_run_version(ctx *C.struct_ddb_ctx) C.int { + return C.ddb_run_version(ctx) +} + +func ddb_run_close(ctx *C.struct_ddb_ctx) C.int { + return C.ddb_run_close(ctx) +} + +func ddb_run_superblock_dump(ctx *C.struct_ddb_ctx) C.int { + return C.ddb_run_superblock_dump(ctx) +} + +func ddb_run_value_dump(ctx *C.struct_ddb_ctx, opts *C.struct_value_dump_options) C.int { + return C.ddb_run_value_dump(ctx, opts) +} + +func ddb_run_rm(ctx *C.struct_ddb_ctx, opts *C.struct_rm_options) C.int { + return C.ddb_run_rm(ctx, opts) +} + +func ddb_run_value_load(ctx *C.struct_ddb_ctx, opts *C.struct_value_load_options) C.int { + return C.ddb_run_value_load(ctx, opts) +} + +func ddb_run_ilog_dump(ctx *C.struct_ddb_ctx, opts *C.struct_ilog_dump_options) C.int { + return C.ddb_run_ilog_dump(ctx, opts) +} + +func ddb_run_ilog_commit(ctx *C.struct_ddb_ctx, opts *C.struct_ilog_commit_options) C.int { + return C.ddb_run_ilog_commit(ctx, opts) +} + +func ddb_run_ilog_clear(ctx *C.struct_ddb_ctx, opts *C.struct_ilog_clear_options) C.int { + return C.ddb_run_ilog_clear(ctx, opts) +} + +func ddb_run_dtx_dump(ctx *C.struct_ddb_ctx, opts *C.struct_dtx_dump_options) C.int { + return C.ddb_run_dtx_dump(ctx, opts) +} + +func ddb_run_dtx_cmt_clear(ctx *C.struct_ddb_ctx, opts *C.struct_dtx_cmt_clear_options) C.int { + return C.ddb_run_dtx_cmt_clear(ctx, opts) +} + +func ddb_run_smd_sync(ctx *C.struct_ddb_ctx, opts *C.struct_smd_sync_options) C.int { + return C.ddb_run_smd_sync(ctx, opts) +} + +func ddb_run_vea_dump(ctx *C.struct_ddb_ctx) C.int { + return C.ddb_run_vea_dump(ctx) +} + +func ddb_run_vea_update(ctx *C.struct_ddb_ctx, opts *C.struct_vea_update_options) C.int { + return C.ddb_run_vea_update(ctx, opts) +} + +func ddb_run_dtx_act_commit(ctx *C.struct_ddb_ctx, opts *C.struct_dtx_act_options) C.int { + return C.ddb_run_dtx_act_commit(ctx, opts) +} + +func ddb_run_dtx_act_abort(ctx *C.struct_ddb_ctx, opts *C.struct_dtx_act_options) C.int { + return C.ddb_run_dtx_act_abort(ctx, opts) +} + +func ddb_feature_string2flags(ctx *C.struct_ddb_ctx, s *C.char, compat *C.uint64_t, incompat *C.uint64_t) C.int { + return C.ddb_feature_string2flags(ctx, s, compat, incompat) +} + +func ddb_run_feature(ctx *C.struct_ddb_ctx, opts *C.struct_feature_options) C.int { + return C.ddb_run_feature(ctx, opts) +} + +func ddb_run_rm_pool(ctx *C.struct_ddb_ctx, opts *C.struct_rm_pool_options) C.int { + return C.ddb_run_rm_pool(ctx, opts) +} + +func ddb_run_dtx_act_discard_invalid(ctx *C.struct_ddb_ctx, opts *C.struct_dtx_act_options) C.int { + return C.ddb_run_dtx_act_discard_invalid(ctx, opts) +} + +func ddb_run_dev_list(ctx *C.struct_ddb_ctx, opts *C.struct_dev_list_options) C.int { + return C.ddb_run_dev_list(ctx, opts) +} + +func ddb_run_dev_replace(ctx *C.struct_ddb_ctx, opts *C.struct_dev_replace_options) C.int { + return C.ddb_run_dev_replace(ctx, opts) +} + +func ddb_run_dtx_stat(ctx *C.struct_ddb_ctx, opts *C.struct_dtx_stat_options) C.int { + return C.ddb_run_dtx_stat(ctx, opts) +} + +func ddb_run_prov_mem(ctx *C.struct_ddb_ctx, opts *C.struct_prov_mem_options) C.int { + return C.ddb_run_prov_mem(ctx, opts) +} + +func ddb_run_dtx_aggr(ctx *C.struct_ddb_ctx, opts *C.struct_dtx_aggr_options) C.int { + return C.ddb_run_dtx_aggr(ctx, opts) +} diff --git a/src/control/cmd/ddb/libddb_stubs.go b/src/control/cmd/ddb/libddb_stubs.go new file mode 100644 index 00000000000..9200a27e540 --- /dev/null +++ b/src/control/cmd/ddb/libddb_stubs.go @@ -0,0 +1,460 @@ +// +// (C) Copyright 2026 Hewlett Packard Enterprise Development LP. +// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// + +//go:build test_stubs + +package main + +/* + #cgo CFLAGS: -I${SRCDIR}/../../../utils/ddb/ + + #include +*/ +import "C" + +// fromGoErr converts a Go error to a C.int return code. +// Non-nil errors are mapped to -1 (generic failure) so that daosError() will +// propagate a non-nil error back to the caller. +func fromGoErr(err error) C.int { + if err == nil { + return 0 + } + return -1 +} + +// resetDdbStubs resets all stub variables to their zero values. +// Call this at the start of each test (via newTestContext) to ensure isolation. +func resetDdbStubs() { + ddb_init_RC = 0 + + ddb_pool_is_open_RC = false + + ddb_run_ls_RC, ddb_run_ls_Fn = 0, nil + ddb_run_open_RC, ddb_run_open_Fn = 0, nil + ddb_run_version_RC, ddb_run_version_Fn = 0, nil + ddb_run_close_RC, ddb_run_close_Fn = 0, nil + ddb_run_superblock_dump_RC, ddb_run_superblock_dump_Fn = 0, nil + ddb_run_value_dump_RC, ddb_run_value_dump_Fn = 0, nil + ddb_run_rm_RC, ddb_run_rm_Fn = 0, nil + ddb_run_value_load_RC, ddb_run_value_load_Fn = 0, nil + ddb_run_ilog_dump_RC, ddb_run_ilog_dump_Fn = 0, nil + ddb_run_ilog_commit_RC, ddb_run_ilog_commit_Fn = 0, nil + ddb_run_ilog_clear_RC, ddb_run_ilog_clear_Fn = 0, nil + ddb_run_dtx_dump_RC, ddb_run_dtx_dump_Fn = 0, nil + ddb_run_dtx_cmt_clear_RC, ddb_run_dtx_cmt_clear_Fn = 0, nil + ddb_run_smd_sync_RC, ddb_run_smd_sync_Fn = 0, nil + ddb_run_vea_dump_RC, ddb_run_vea_dump_Fn = 0, nil + ddb_run_vea_update_RC, ddb_run_vea_update_Fn = 0, nil + ddb_run_dtx_act_commit_RC, ddb_run_dtx_act_commit_Fn = 0, nil + ddb_run_dtx_act_abort_RC, ddb_run_dtx_act_abort_Fn = 0, nil + ddb_feature_string2flags_RC, ddb_feature_string2flags_Fn = 0, nil + ddb_run_feature_RC, ddb_run_feature_Fn = 0, nil + ddb_run_rm_pool_RC, ddb_run_rm_pool_Fn = 0, nil + ddb_run_dtx_act_discard_invalid_RC, ddb_run_dtx_act_discard_invalid_Fn = 0, nil + ddb_run_dev_list_RC, ddb_run_dev_list_Fn = 0, nil + ddb_run_dev_replace_RC, ddb_run_dev_replace_Fn = 0, nil + ddb_run_dtx_stat_RC, ddb_run_dtx_stat_Fn = 0, nil + ddb_run_prov_mem_RC, ddb_run_prov_mem_Fn = 0, nil + ddb_run_dtx_aggr_RC, ddb_run_dtx_aggr_Fn = 0, nil +} + +var ddb_init_RC C.int = 0 + +func ddb_init() C.int { return ddb_init_RC } +func ddb_fini() {} +func ddb_ctx_init(_ *C.struct_ddb_ctx) {} + +var ddb_pool_is_open_RC = false + +func ddb_pool_is_open(_ *C.struct_ddb_ctx) C.bool { + return C.bool(ddb_pool_is_open_RC) +} + +var ( + ddb_run_ls_RC C.int = 0 + ddb_run_ls_Fn func(path string, recursive bool, details bool) error +) + +func ddb_run_ls(_ *C.struct_ddb_ctx, opts *C.struct_ls_options) C.int { + if ddb_run_ls_Fn != nil { + return fromGoErr(ddb_run_ls_Fn( + C.GoString(opts.path), + bool(opts.recursive), + bool(opts.details), + )) + } + return ddb_run_ls_RC +} + +var ( + ddb_run_open_RC C.int = 0 + ddb_run_open_Fn func(path string, dbPath string, writeMode bool) error +) + +func ddb_run_open(_ *C.struct_ddb_ctx, opts *C.struct_open_options) C.int { + if ddb_run_open_Fn != nil { + return fromGoErr(ddb_run_open_Fn( + C.GoString(opts.path), + C.GoString(opts.db_path), + bool(opts.write_mode), + )) + } + return ddb_run_open_RC +} + +var ( + ddb_run_version_RC C.int = 0 + ddb_run_version_Fn func() error +) + +func ddb_run_version(_ *C.struct_ddb_ctx) C.int { + if ddb_run_version_Fn != nil { + return fromGoErr(ddb_run_version_Fn()) + } + return ddb_run_version_RC +} + +var ( + ddb_run_close_RC C.int = 0 + ddb_run_close_Fn func() error +) + +func ddb_run_close(_ *C.struct_ddb_ctx) C.int { + if ddb_run_close_Fn != nil { + return fromGoErr(ddb_run_close_Fn()) + } + return ddb_run_close_RC +} + +var ( + ddb_run_superblock_dump_RC C.int = 0 + ddb_run_superblock_dump_Fn func() error +) + +func ddb_run_superblock_dump(_ *C.struct_ddb_ctx) C.int { + if ddb_run_superblock_dump_Fn != nil { + return fromGoErr(ddb_run_superblock_dump_Fn()) + } + return ddb_run_superblock_dump_RC +} + +var ( + ddb_run_value_dump_RC C.int = 0 + ddb_run_value_dump_Fn func(path string, dst string) error +) + +func ddb_run_value_dump(_ *C.struct_ddb_ctx, opts *C.struct_value_dump_options) C.int { + if ddb_run_value_dump_Fn != nil { + return fromGoErr(ddb_run_value_dump_Fn( + C.GoString(opts.path), + C.GoString(opts.dst), + )) + } + return ddb_run_value_dump_RC +} + +var ( + ddb_run_rm_RC C.int = 0 + ddb_run_rm_Fn func(path string) error +) + +func ddb_run_rm(_ *C.struct_ddb_ctx, opts *C.struct_rm_options) C.int { + if ddb_run_rm_Fn != nil { + return fromGoErr(ddb_run_rm_Fn(C.GoString(opts.path))) + } + return ddb_run_rm_RC +} + +var ( + ddb_run_value_load_RC C.int = 0 + ddb_run_value_load_Fn func(src string, dst string) error +) + +func ddb_run_value_load(_ *C.struct_ddb_ctx, opts *C.struct_value_load_options) C.int { + if ddb_run_value_load_Fn != nil { + return fromGoErr(ddb_run_value_load_Fn( + C.GoString(opts.src), + C.GoString(opts.dst), + )) + } + return ddb_run_value_load_RC +} + +var ( + ddb_run_ilog_dump_RC C.int = 0 + ddb_run_ilog_dump_Fn func(path string) error +) + +func ddb_run_ilog_dump(_ *C.struct_ddb_ctx, opts *C.struct_ilog_dump_options) C.int { + if ddb_run_ilog_dump_Fn != nil { + return fromGoErr(ddb_run_ilog_dump_Fn(C.GoString(opts.path))) + } + return ddb_run_ilog_dump_RC +} + +var ( + ddb_run_ilog_commit_RC C.int = 0 + ddb_run_ilog_commit_Fn func(path string) error +) + +func ddb_run_ilog_commit(_ *C.struct_ddb_ctx, opts *C.struct_ilog_commit_options) C.int { + if ddb_run_ilog_commit_Fn != nil { + return fromGoErr(ddb_run_ilog_commit_Fn(C.GoString(opts.path))) + } + return ddb_run_ilog_commit_RC +} + +var ( + ddb_run_ilog_clear_RC C.int = 0 + ddb_run_ilog_clear_Fn func(path string) error +) + +func ddb_run_ilog_clear(_ *C.struct_ddb_ctx, opts *C.struct_ilog_clear_options) C.int { + if ddb_run_ilog_clear_Fn != nil { + return fromGoErr(ddb_run_ilog_clear_Fn(C.GoString(opts.path))) + } + return ddb_run_ilog_clear_RC +} + +var ( + ddb_run_dtx_dump_RC C.int = 0 + ddb_run_dtx_dump_Fn func(path string, active bool, committed bool) error +) + +func ddb_run_dtx_dump(_ *C.struct_ddb_ctx, opts *C.struct_dtx_dump_options) C.int { + if ddb_run_dtx_dump_Fn != nil { + return fromGoErr(ddb_run_dtx_dump_Fn( + C.GoString(opts.path), + bool(opts.active), + bool(opts.committed), + )) + } + return ddb_run_dtx_dump_RC +} + +var ( + ddb_run_dtx_cmt_clear_RC C.int = 0 + ddb_run_dtx_cmt_clear_Fn func(path string) error +) + +func ddb_run_dtx_cmt_clear(_ *C.struct_ddb_ctx, opts *C.struct_dtx_cmt_clear_options) C.int { + if ddb_run_dtx_cmt_clear_Fn != nil { + return fromGoErr(ddb_run_dtx_cmt_clear_Fn(C.GoString(opts.path))) + } + return ddb_run_dtx_cmt_clear_RC +} + +var ( + ddb_run_smd_sync_RC C.int = 0 + ddb_run_smd_sync_Fn func(nvmeConf string, dbPath string) error +) + +func ddb_run_smd_sync(_ *C.struct_ddb_ctx, opts *C.struct_smd_sync_options) C.int { + if ddb_run_smd_sync_Fn != nil { + return fromGoErr(ddb_run_smd_sync_Fn( + C.GoString(opts.nvme_conf), + C.GoString(opts.db_path), + )) + } + return ddb_run_smd_sync_RC +} + +var ( + ddb_run_vea_dump_RC C.int = 0 + ddb_run_vea_dump_Fn func() error +) + +func ddb_run_vea_dump(_ *C.struct_ddb_ctx) C.int { + if ddb_run_vea_dump_Fn != nil { + return fromGoErr(ddb_run_vea_dump_Fn()) + } + return ddb_run_vea_dump_RC +} + +var ( + ddb_run_vea_update_RC C.int = 0 + ddb_run_vea_update_Fn func(offset string, blkCnt string) error +) + +func ddb_run_vea_update(_ *C.struct_ddb_ctx, opts *C.struct_vea_update_options) C.int { + if ddb_run_vea_update_Fn != nil { + return fromGoErr(ddb_run_vea_update_Fn( + C.GoString(opts.offset), + C.GoString(opts.blk_cnt), + )) + } + return ddb_run_vea_update_RC +} + +var ( + ddb_run_dtx_act_commit_RC C.int = 0 + ddb_run_dtx_act_commit_Fn func(path string, dtxID string) error +) + +func ddb_run_dtx_act_commit(_ *C.struct_ddb_ctx, opts *C.struct_dtx_act_options) C.int { + if ddb_run_dtx_act_commit_Fn != nil { + return fromGoErr(ddb_run_dtx_act_commit_Fn( + C.GoString(opts.path), + C.GoString(opts.dtx_id), + )) + } + return ddb_run_dtx_act_commit_RC +} + +var ( + ddb_run_dtx_act_abort_RC C.int = 0 + ddb_run_dtx_act_abort_Fn func(path string, dtxID string) error +) + +func ddb_run_dtx_act_abort(_ *C.struct_ddb_ctx, opts *C.struct_dtx_act_options) C.int { + if ddb_run_dtx_act_abort_Fn != nil { + return fromGoErr(ddb_run_dtx_act_abort_Fn( + C.GoString(opts.path), + C.GoString(opts.dtx_id), + )) + } + return ddb_run_dtx_act_abort_RC +} + +var ( + ddb_feature_string2flags_RC C.int = 0 + ddb_feature_string2flags_Fn func(s string) (compat uint64, incompat uint64, err error) +) + +func ddb_feature_string2flags(_ *C.struct_ddb_ctx, s *C.char, compat *C.uint64_t, incompat *C.uint64_t) C.int { + if ddb_feature_string2flags_Fn != nil { + c, ic, err := ddb_feature_string2flags_Fn(C.GoString(s)) + if err != nil { + return fromGoErr(err) + } + *compat = C.uint64_t(c) + *incompat = C.uint64_t(ic) + return 0 + } + return ddb_feature_string2flags_RC +} + +var ( + ddb_run_feature_RC C.int = 0 + ddb_run_feature_Fn func(path, dbPath, enable, disable string, show bool) error +) + +func ddb_run_feature(_ *C.struct_ddb_ctx, opts *C.struct_feature_options) C.int { + if ddb_run_feature_Fn != nil { + return fromGoErr(ddb_run_feature_Fn( + C.GoString(opts.path), + C.GoString(opts.db_path), + "", "", + bool(opts.show_features), + )) + } + return ddb_run_feature_RC +} + +var ( + ddb_run_rm_pool_RC C.int = 0 + ddb_run_rm_pool_Fn func(path string, dbPath string) error +) + +func ddb_run_rm_pool(_ *C.struct_ddb_ctx, opts *C.struct_rm_pool_options) C.int { + if ddb_run_rm_pool_Fn != nil { + return fromGoErr(ddb_run_rm_pool_Fn( + C.GoString(opts.path), + C.GoString(opts.db_path), + )) + } + return ddb_run_rm_pool_RC +} + +var ( + ddb_run_dtx_act_discard_invalid_RC C.int = 0 + ddb_run_dtx_act_discard_invalid_Fn func(path string, dtxID string) error +) + +func ddb_run_dtx_act_discard_invalid(_ *C.struct_ddb_ctx, opts *C.struct_dtx_act_options) C.int { + if ddb_run_dtx_act_discard_invalid_Fn != nil { + return fromGoErr(ddb_run_dtx_act_discard_invalid_Fn( + C.GoString(opts.path), + C.GoString(opts.dtx_id), + )) + } + return ddb_run_dtx_act_discard_invalid_RC +} + +var ( + ddb_run_dev_list_RC C.int = 0 + ddb_run_dev_list_Fn func(dbPath string) error +) + +func ddb_run_dev_list(_ *C.struct_ddb_ctx, opts *C.struct_dev_list_options) C.int { + if ddb_run_dev_list_Fn != nil { + return fromGoErr(ddb_run_dev_list_Fn(C.GoString(opts.db_path))) + } + return ddb_run_dev_list_RC +} + +var ( + ddb_run_dev_replace_RC C.int = 0 + ddb_run_dev_replace_Fn func(dbPath string, oldDevid string, newDevid string) error +) + +func ddb_run_dev_replace(_ *C.struct_ddb_ctx, opts *C.struct_dev_replace_options) C.int { + if ddb_run_dev_replace_Fn != nil { + return fromGoErr(ddb_run_dev_replace_Fn( + C.GoString(opts.db_path), + C.GoString(opts.old_devid), + C.GoString(opts.new_devid), + )) + } + return ddb_run_dev_replace_RC +} + +var ( + ddb_run_dtx_stat_RC C.int = 0 + ddb_run_dtx_stat_Fn func(path string, details bool) error +) + +func ddb_run_dtx_stat(_ *C.struct_ddb_ctx, opts *C.struct_dtx_stat_options) C.int { + if ddb_run_dtx_stat_Fn != nil { + return fromGoErr(ddb_run_dtx_stat_Fn( + C.GoString(opts.path), + bool(opts.details), + )) + } + return ddb_run_dtx_stat_RC +} + +var ( + ddb_run_prov_mem_RC C.int = 0 + ddb_run_prov_mem_Fn func(dbPath string, tmpfsMount string, tmpfsMountSize uint) error +) + +func ddb_run_prov_mem(_ *C.struct_ddb_ctx, opts *C.struct_prov_mem_options) C.int { + if ddb_run_prov_mem_Fn != nil { + return fromGoErr(ddb_run_prov_mem_Fn( + C.GoString(opts.db_path), + C.GoString(opts.tmpfs_mount), + uint(opts.tmpfs_mount_size), + )) + } + return ddb_run_prov_mem_RC +} + +var ( + ddb_run_dtx_aggr_RC C.int = 0 + ddb_run_dtx_aggr_Fn func(path string, cmtTime uint64, cmtDate string) error +) + +func ddb_run_dtx_aggr(_ *C.struct_ddb_ctx, opts *C.struct_dtx_aggr_options) C.int { + if ddb_run_dtx_aggr_Fn != nil { + return fromGoErr(ddb_run_dtx_aggr_Fn( + C.GoString(opts.path), + uint64(opts.cmt_time), + C.GoString(opts.cmt_date), + )) + } + return ddb_run_dtx_aggr_RC +} diff --git a/src/control/cmd/ddb/main.go b/src/control/cmd/ddb/main.go index 9d20ce3b3d1..67cc415069b 100644 --- a/src/control/cmd/ddb/main.go +++ b/src/control/cmd/ddb/main.go @@ -12,9 +12,9 @@ import ( "fmt" "io" "os" - "path" "path/filepath" "runtime/debug" + "slices" "strings" "github.com/desertbit/columnize" @@ -30,17 +30,33 @@ import ( "github.com/daos-stack/daos/src/control/server/engine" ) +var errHelpRequested = errors.New("help requested") + +type unknownCmdError struct { + cmd string +} + +func (e *unknownCmdError) Error() string { + return fmt.Sprintf("Unknown command: '%s'. Run 'help' to see available commands.", e.cmd) +} + func exitWithError(err error) { - cmdName := path.Base(os.Args[0]) - fmt.Fprintf(os.Stderr, "ERROR: %s: %v\n", cmdName, err) + cmdName := filepath.Base(os.Args[0]) + msg := fmt.Sprintf("ERROR: %s: %v", cmdName, err) if fault.HasResolution(err) { - fmt.Fprintf(os.Stderr, "ERROR: %s: %s", cmdName, fault.ShowResolutionFor(err)) + msg = fmt.Sprintf("%s (%s)", msg, fault.ShowResolutionFor(err)) + } + fmt.Fprintln(os.Stderr, msg) + + if _, ok := err.(*unknownCmdError); ok { + app := createGrumbleApp(nil) + printCommands(os.Stderr, app) } os.Exit(1) } type cliOptions struct { - WriteMode bool `long:"write_mode" short:"w" description:"Open the vos file in write mode."` + WriteMode bool `long:"write_mode" short:"w" description:"Open the VOS file in write mode."` CmdFile string `long:"cmd_file" short:"f" description:"Path to a file containing a sequence of ddb commands to execute."` SysdbPath string `long:"db_path" short:"p" description:"Path to the sys db."` VosPath string `long:"vos_path" short:"s" description:"Path to the VOS file to open."` @@ -54,12 +70,12 @@ type cliOptions struct { } const helpCommandsHeader = ` -Available commands: +Available Commands: ` const helpTreePath = ` -Path +VOS Paths: Many of the commands take a VOS tree path. The format for this path is [cont]/[obj]/[dkey]/[akey]/[extent]. To make it easier to navigate the tree, indexes can be used @@ -87,8 +103,13 @@ MODE section of the manpage for details. ` const grumbleUnknownCmdErr = "unknown command, try 'help'" +const runCmdArgsErr = "Cannot use both command file and a command string" +const vosPathMissErr = "Cannot use sys db path without a VOS path" +const loggerInitErr = "Logging facilities cannot be initialized" +const ctxInitErr = "DDB Context cannot be initialized" +const vosPathOpenErr = "Error opening VOS path '%s'" -func runFileCmds(log logging.Logger, app *grumble.App, fileName string) error { +func runFileCmds(app *grumble.App, log logging.Logger, fileName string) error { file, err := os.Open(fileName) if err != nil { return errors.Wrapf(err, "Error opening file %q", fileName) @@ -115,6 +136,9 @@ func runFileCmds(log logging.Logger, app *grumble.App, fileName string) error { log.Debugf("Running Command %q\n", lineStr) err = runCmdStr(app, nil, lineCmd[0], lineCmd[1:]...) if err != nil { + if err.Error() == grumbleUnknownCmdErr { + return errors.Wrapf(&unknownCmdError{cmd: lineCmd[0]}, "Failed running command %q", lineStr) + } return errors.Wrapf(err, "Failed running command %q", lineStr) } } @@ -147,37 +171,25 @@ func printGeneralHelp(app *grumble.App, generalMsg string) { // Ask grumble to generate a help message for the requested command. // Caveat: There is no known easy way of forcing grumble to use log to print the generated message // so the output goes directly to stdout. -// Returns false in case the opts.Args.RunCmd is unknown. -func printCmdHelp(app *grumble.App, opts *cliOptions) bool { - err := runCmdStr(app, nil, string(opts.Args.RunCmd), "--help") - if err != nil { - if err.Error() == grumbleUnknownCmdErr { - fmt.Fprintf(os.Stderr, "ERROR: Unknown command '%s'", string(opts.Args.RunCmd)) - printCommands(os.Stderr, app) - } else { - fmt.Fprintf(os.Stderr, "ERROR: %s", err.Error()) - } - return false +func printCmdHelp(app *grumble.App, opts cliOptions) error { + if err := runCmdStr(app, nil, string(opts.Args.RunCmd), "--help"); err != nil { + return &unknownCmdError{cmd: opts.Args.RunCmd} } - return true + return errHelpRequested } // Prints either general or command-specific help message. // Returns a reasonable return code in case the caller chooses to terminate the process. -func printHelp(generalMsg string, opts *cliOptions) int { +func printHelp(opts cliOptions, generalMsg string) error { // ctx is not necessary since this instance of the app is not intended to run any of the commands app := createGrumbleApp(nil) if string(opts.Args.RunCmd) == "" { printGeneralHelp(app, generalMsg) - return 0 + return errHelpRequested } - if printCmdHelp(app, opts) { - return 0 - } else { - return 1 - } + return printCmdHelp(app, opts) } func setenvIfNotSet(key, value string) { @@ -216,7 +228,7 @@ func strToLogLevels(level string) (logging.LogLevel, engine.LogLevel, error) { } } -func newLogger(opts *cliOptions) (*logging.LeveledLogger, error) { +func newLogger(opts cliOptions) (*logging.LeveledLogger, error) { level := "ERR" if opts.Debug != "" { level = opts.Debug @@ -261,94 +273,83 @@ func newLogger(opts *cliOptions) (*logging.LeveledLogger, error) { return fileLog, nil } -func parseOpts(args []string, opts *cliOptions) error { - p := flags.NewParser(opts, flags.HelpFlag|flags.IgnoreUnknown) - p.Name = "ddb" - p.Usage = "[OPTIONS]" - p.ShortDescription = "daos debug tool" - p.LongDescription = ddbLongDescription +func closePoolIfOpen(ctx *DdbContext, log *logging.LeveledLogger) { + if !ctx.PoolIsOpen() { + return + } - // Set the traceback level such that a crash results in - // a coredump (when ulimit -c is set appropriately). - debug.SetTraceback("crash") + log.Info("Closing pool...\n") + if err := ctx.Close(); err != nil { + log.Errorf("Error closing pool: %s\n", err) + } +} - if _, err := p.ParseArgs(args); err != nil { +func parseOpts(args []string, ctx *DdbContext) (cliOptions, *flags.Parser, error) { + var opts cliOptions + parser := flags.NewParser(&opts, flags.HelpFlag|flags.IgnoreUnknown) + parser.Name = "ddb" + parser.Usage = "[OPTIONS]" + parser.ShortDescription = "daos debug tool" + parser.LongDescription = ddbLongDescription + + if _, err := parser.ParseArgs(args); err != nil { if fe, ok := errors.Cause(err).(*flags.Error); ok && fe.Type == flags.ErrHelp { - os.Exit(printHelp(fe.Error(), opts)) + return opts, nil, printHelp(opts, fe.Error()) } - return err - } - - if opts.Version { - opts.Args.RunCmd = "version" - opts.Args.RunCmdArgs = []string{} - opts.CmdFile = "" + return opts, nil, err } if opts.Args.RunCmd != "" && opts.CmdFile != "" { - return errors.New("Cannot use both command file and a command string") + return opts, nil, errors.New(runCmdArgsErr) } - log, err := newLogger(opts) - if err != nil { - return errors.Wrap(err, "Error configuring logging") + if opts.VosPath == "" && opts.SysdbPath != "" { + return opts, nil, errors.New(vosPathMissErr) } - log.Debug("Logging facilities initialized") - var ( - ctx *DdbContext - cleanup func() - ) - if ctx, cleanup, err = InitDdb(log); err != nil { - return errors.Wrap(err, "Error initializing the DDB Context") + return opts, parser, nil +} + +func run(ctx *DdbContext, log *logging.LeveledLogger, opts cliOptions, parser *flags.Parser) error { + cleanup, err := ctx.Init() + if err != nil { + return errors.Wrap(err, ctxInitErr) } defer cleanup() app := createGrumbleApp(ctx) - if opts.SysdbPath != "" { - cleanup := SetCString(&ctx.ctx.dc_db_path, string(opts.SysdbPath)) - defer cleanup() - } - if opts.VosPath != "" { - cleanup := SetCString(&ctx.ctx.dc_pool_path, string(opts.VosPath)) - defer cleanup() - - if !strings.HasPrefix(string(opts.Args.RunCmd), "feature") && - !strings.HasPrefix(string(opts.Args.RunCmd), "open") && - !strings.HasPrefix(string(opts.Args.RunCmd), "close") && - !strings.HasPrefix(string(opts.Args.RunCmd), "prov_mem") && - !strings.HasPrefix(string(opts.Args.RunCmd), "smd_sync") && - !strings.HasPrefix(string(opts.Args.RunCmd), "rm_pool") && - !strings.HasPrefix(string(opts.Args.RunCmd), "dev_list") && - !strings.HasPrefix(string(opts.Args.RunCmd), "dev_replace") { + // Commands that manage the pool open/close lifecycle themselves and must + // not have the pool pre-opened by the CLI layer. + noAutoOpen := []string{ + "feature", + "open", + "close", + "prov_mem", + "smd_sync", + "rm_pool", + "dev_list", + "dev_replace", + } + if !slices.Contains(noAutoOpen, opts.Args.RunCmd) { log.Debugf("Connect to path: %s\n", opts.VosPath) - if err := ddbOpen(ctx, string(opts.VosPath), bool(opts.WriteMode)); err != nil { - return errors.Wrapf(err, "Error opening path: %s", opts.VosPath) + if err := ctx.Open(string(opts.VosPath), string(opts.SysdbPath), opts.WriteMode); err != nil { + return errors.Wrapf(err, vosPathOpenErr, opts.VosPath) } + defer closePoolIfOpen(ctx, log) } } if opts.Args.RunCmd != "" || opts.CmdFile != "" { // Non-interactive mode if opts.Args.RunCmd != "" { - err := runCmdStr(app, p, string(opts.Args.RunCmd), opts.Args.RunCmdArgs...) - if err != nil { - log.Errorf("Error running command %q %s\n", string(opts.Args.RunCmd), err) - } + err = runCmdStr(app, parser, string(opts.Args.RunCmd), opts.Args.RunCmdArgs...) } else { - err := runFileCmds(log, app, opts.CmdFile) - if err != nil { - log.Errorf("Error running command file: %s\n", err) - } + err = runFileCmds(app, log, opts.CmdFile) } - - if ddbPoolIsOpen(ctx) { - err := ddbClose(ctx) - if err != nil { - log.Error("Error closing pool\n") - } + if err != nil && err.Error() == grumbleUnknownCmdErr { + return &unknownCmdError{cmd: opts.Args.RunCmd} } return err } @@ -360,24 +361,41 @@ func parseOpts(args []string, opts *cliOptions) error { os.Args = []string{} result := app.Run() // make sure pool is closed - if ddbPoolIsOpen(ctx) { - err := ddbClose(ctx) - if err != nil { - log.Error("Error closing pool\n") - } - } + closePoolIfOpen(ctx, log) return result } func main() { - var opts cliOptions + // Set the traceback level such that a crash results in + // a coredump (when ulimit -c is set appropriately). + debug.SetTraceback("crash") // Must be called before any write to stdout. if err := logging.DisableCStdoutBuffering(); err != nil { exitWithError(err) } - if err := parseOpts(os.Args[1:], &opts); err != nil { + ctx := &DdbContext{} + opts, parser, err := parseOpts(os.Args[1:], ctx) + if errors.Is(err, errHelpRequested) { + return + } + if err != nil { + exitWithError(err) + } + + if opts.Version { + fmt.Printf("ddb version %s\n", build.DaosVersion) + return + } + + log, err := newLogger(opts) + if err != nil { + exitWithError(errors.Wrap(err, loggerInitErr)) + } + log.Debug("Logging facilities initialized") + + if err = run(ctx, log, opts, parser); err != nil { exitWithError(err) } } diff --git a/src/utils/ddb/ddb.h b/src/utils/ddb/ddb.h index c079b25632b..7107dcc4dc3 100644 --- a/src/utils/ddb/ddb.h +++ b/src/utils/ddb/ddb.h @@ -64,11 +64,9 @@ struct ddb_io_ft { }; struct ddb_ctx { - struct ddb_io_ft dc_io_ft; - daos_handle_t dc_poh; - bool dc_write_mode; - const char *dc_pool_path; - const char *dc_db_path; + struct ddb_io_ft dc_io_ft; + daos_handle_t dc_poh; + bool dc_write_mode; }; void ddb_ctx_init(struct ddb_ctx *ctx); diff --git a/src/utils/ddb/ddb_commands.c b/src/utils/ddb/ddb_commands.c index fc5477f6e94..669a3eb670b 100644 --- a/src/utils/ddb/ddb_commands.c +++ b/src/utils/ddb/ddb_commands.c @@ -73,20 +73,11 @@ ddb_pool_is_open(struct ddb_ctx *ctx) int ddb_run_open(struct ddb_ctx *ctx, struct open_options *opt) { - struct vos_file_parts path_parts = {0}; - int rc; - DDB_POOL_SHOULD_CLOSE(ctx); - rc = parse_vos_file_parts(opt->path, opt->db_path, &path_parts); - if (!SUCCESS(rc)) - return rc; - - DDB_CAN_PROCEED(ctx, path_parts.vf_db_path); - ctx->dc_write_mode = opt->write_mode; - - return dv_pool_open(opt->path, &path_parts, &ctx->dc_poh, 0, ctx->dc_write_mode); + DDB_CAN_PROCEED(ctx, opt->db_path); + return dv_pool_open(opt->path, opt->db_path, &ctx->dc_poh, 0, opt->write_mode); } int @@ -1082,7 +1073,6 @@ feature_write_action(struct feature_options *opt) int ddb_run_feature(struct ddb_ctx *ctx, struct feature_options *opt) { - struct vos_file_parts path_parts = {0}; int rc; uint64_t new_compat_flags; uint64_t new_incompat_flags; @@ -1101,22 +1091,13 @@ ddb_run_feature(struct ddb_ctx *ctx, struct feature_options *opt) if (feature_write_action(opt) && !ctx->dc_write_mode) return -DER_NO_PERM; - if (!opt->path || strnlen(opt->path, PATH_MAX) == 0) - opt->path = ctx->dc_pool_path; - - if (!opt->db_path || strnlen(opt->db_path, PATH_MAX) == 0) - opt->db_path = ctx->dc_db_path; - - rc = parse_vos_file_parts(opt->path, opt->db_path, &path_parts); - if (!SUCCESS(rc)) - return rc; - - DDB_CAN_PROCEED(ctx, path_parts.vf_db_path); - - rc = dv_pool_open(opt->path, &path_parts, &ctx->dc_poh, VOS_POF_FOR_FEATURE_FLAG, + DDB_CAN_PROCEED(ctx, opt->db_path); + rc = dv_pool_open(opt->path, opt->db_path, &ctx->dc_poh, VOS_POF_FOR_FEATURE_FLAG, ctx->dc_write_mode); - if (rc) + if (rc) { + ddb_errorf(ctx, "Unable to open VOS pool '%s'\n", opt->path); return rc; + } close = true; skip: @@ -1160,18 +1141,9 @@ ddb_run_feature(struct ddb_ctx *ctx, struct feature_options *opt) int ddb_run_rm_pool(struct ddb_ctx *ctx, struct rm_pool_options *opt) { - struct vos_file_parts path_parts = {0}; - int rc; - DDB_POOL_SHOULD_CLOSE(ctx); - rc = parse_vos_file_parts(opt->path, opt->db_path, &path_parts); - if (!SUCCESS(rc)) - return rc; - - DDB_CAN_PROCEED(ctx, path_parts.vf_db_path); - - return dv_pool_destroy(opt->path, &path_parts); + return dv_pool_destroy(opt->path, opt->db_path, ctx); } #define DTI_ALL "all" @@ -1660,7 +1632,7 @@ ddb_run_dtx_stat(struct ddb_ctx *ctx, struct dtx_stat_options *opt) rc = vos_iterate(¶m, VOS_ITER_COUUID, false, &anchors, NULL, dtx_stat_cont_cb, &args, NULL); } while (rc > 0); - ddb_printf(ctx, "DTX entries statistics of the pool %s\n", ctx->dc_pool_path); + ddb_print(ctx, "DTX entries statistics of the pool:\n"); if (opt->details) rc = dtx_stat_print(ctx, args.cmt_cnt, &args.time_stat, args.aggr_epoch); else diff --git a/src/utils/ddb/ddb_parse.c b/src/utils/ddb/ddb_parse.c index 67950a90ab2..5e5946fd400 100644 --- a/src/utils/ddb/ddb_parse.c +++ b/src/utils/ddb/ddb_parse.c @@ -195,6 +195,7 @@ parse_vos_file_parts(const char *vos_path, const char *db_path, regex_t preg; regmatch_t match[MATCH_SIZE]; struct vos_file_parts *vfp_tmp; + size_t vos_path_len; int rc; D_ASSERT(vos_path != NULL && vos_file_parts != NULL); @@ -217,6 +218,15 @@ parse_vos_file_parts(const char *vos_path, const char *db_path, } D_ASSERT(rc == 0); + vos_path_len = strnlen(vos_path, VOS_PATH_SIZE); + if (vos_path_len >= VOS_PATH_SIZE) { + D_ERROR("VOS path '%s' too long: got=%zu, max=%d\n", vos_path, vos_path_len, + VOS_PATH_SIZE - 1); + rc = -DER_EXCEEDS_PATH_LEN; + goto out_preg; + } + memcpy(vfp_tmp->vf_vos_file_path, vos_path, vos_path_len + 1); + if (db_path != NULL && db_path[0] != '\0') { size_t db_path_len = strnlen(db_path, PATH_MAX); if (db_path_len >= DB_PATH_SIZE) { diff --git a/src/utils/ddb/ddb_parse.h b/src/utils/ddb/ddb_parse.h index 15937d0a562..c7da5fda375 100644 --- a/src/utils/ddb/ddb_parse.h +++ b/src/utils/ddb/ddb_parse.h @@ -15,8 +15,9 @@ #include #include "ddb_common.h" -enum { DB_PATH_SIZE = 256, VOS_FILE_NAME_SIZE = 16 }; +enum { VOS_PATH_SIZE = PATH_MAX, DB_PATH_SIZE = PATH_MAX, VOS_FILE_NAME_SIZE = 16 }; struct vos_file_parts { + char vf_vos_file_path[VOS_PATH_SIZE]; char vf_db_path[DB_PATH_SIZE]; uuid_t vf_pool_uuid; char vf_vos_file_name[VOS_FILE_NAME_SIZE]; @@ -25,8 +26,8 @@ struct vos_file_parts { /* Parse a path to a VOS file to get needed parts for initializing vos */ int - parse_vos_file_parts(const char *vos_path, const char *db_path, - struct vos_file_parts *vos_file_parts); + parse_vos_file_parts(const char *vos_path, const char *db_path, + struct vos_file_parts *vos_file_parts); /* See ddb_key_to_printable_buf for how the keys will be printed */ int ddb_parse_key(const char *input, daos_key_t *key); diff --git a/src/utils/ddb/ddb_vos.c b/src/utils/ddb/ddb_vos.c index 65f567a39e9..15a5e9c62ee 100644 --- a/src/utils/ddb/ddb_vos.c +++ b/src/utils/ddb/ddb_vos.c @@ -27,12 +27,45 @@ vos_iterate(param, iter_type, recursive, \ anchors, cb, NULL, args, NULL) +static int +create_vos_file_parts(const char *path, const char *db_path, struct vos_file_parts **vf_ptr) +{ + struct vos_file_parts *vf; + int rc; + + D_ALLOC_PTR(vf); + if (vf == NULL) { + D_ERROR("Unable to allocate memory for pool path\n"); + rc = -DER_NOMEM; + goto out; + } + + rc = parse_vos_file_parts(path, db_path, vf); + if (SUCCESS(rc)) { + *vf_ptr = vf; + } else { + D_ERROR("Unable to parse VOS pool path '%s' and DB path '%s'\n", path, + db_path ? db_path : "(null)"); + D_FREE(vf); + } + +out: + return rc; +} + +bool +vmd_wa_can_proceed(struct ddb_ctx *ctx, const char *db_path); + int -dv_pool_open(const char *path, struct vos_file_parts *path_parts, daos_handle_t *poh, - uint32_t flags, bool write_mode) +dv_pool_open(const char *path, const char *db_path, daos_handle_t *poh, uint32_t flags, + bool write_mode) { - int cow_val; - int rc; + int rc; + struct vos_file_parts *vf; + + rc = create_vos_file_parts(path, db_path, &vf); + if (!SUCCESS(rc)) + goto out; /** * When the user requests read‑only mode (write_mode == false), DDB itself will not attempt @@ -50,57 +83,73 @@ dv_pool_open(const char *path, struct vos_file_parts *path_parts, daos_handle_t * the mapped memory do not propagate to the persistent medium. */ if (!write_mode) { - cow_val = 1; - rc = pmemobj_ctl_set(NULL, "copy_on_write.at_open", &cow_val); + int cow_val = 1; + rc = pmemobj_ctl_set(NULL, "copy_on_write.at_open", &cow_val); if (rc != 0) { - return daos_errno2der(errno); + rc = daos_errno2der(errno); + goto out_vf; } } - rc = vos_self_init(path_parts->vf_db_path, true, path_parts->vf_target_idx); + rc = vos_self_init(vf->vf_db_path, true, vf->vf_target_idx); if (!SUCCESS(rc)) { - D_ERROR("Failed to initialize VOS with path '%s': " DF_RC "\n", - path_parts->vf_db_path, DP_RC(rc)); - goto exit; + D_ERROR("Failed to initialize VOS with DB path '%s': " DF_RC "\n", vf->vf_db_path, + DP_RC(rc)); + goto out_cow; } - rc = vos_pool_open(path, path_parts->vf_pool_uuid, flags, poh); + rc = vos_pool_open(vf->vf_vos_file_path, vf->vf_pool_uuid, flags, poh); if (!SUCCESS(rc)) { D_ERROR("Failed to open pool: "DF_RC"\n", DP_RC(rc)); vos_self_fini(); } -exit: +out_cow: if (!write_mode) { /** Restore the default value. */ - cow_val = 0; + int cow_val = 0; pmemobj_ctl_set(NULL, "copy_on_write.at_open", &cow_val); } - +out_vf: + D_FREE(vf); +out: return rc; } int -dv_pool_destroy(const char *path, struct vos_file_parts *path_parts) +dv_pool_destroy(const char *path, const char *db_path, struct ddb_ctx *ctx) { - int rc, flags = 0; + struct vos_file_parts *vf; + int flags = 0; + int rc; + + rc = create_vos_file_parts(path, db_path, &vf); + if (!SUCCESS(rc)) + goto out; + + if (!vmd_wa_can_proceed(ctx, vf->vf_db_path)) { + rc = -DER_NO_SERVICE; + goto out_vf; + } - rc = vos_self_init(path_parts->vf_db_path, true, path_parts->vf_target_idx); + rc = vos_self_init(vf->vf_db_path, true, vf->vf_target_idx); if (!SUCCESS(rc)) { - D_ERROR("Failed to initialize VOS with path '%s': " DF_RC "\n", - path_parts->vf_db_path, DP_RC(rc)); - return rc; + D_ERROR("Failed to initialize VOS with DB path '%s': " DF_RC "\n", vf->vf_db_path, + DP_RC(rc)); + goto out_vf; } - if (strncmp(path_parts->vf_vos_file_name, "rdb", 3) == 0) + if (strncmp(vf->vf_vos_file_name, "rdb", 3) == 0) flags |= VOS_POF_RDB; - rc = vos_pool_destroy_ex(path, path_parts->vf_pool_uuid, flags); + rc = vos_pool_destroy_ex(vf->vf_vos_file_path, vf->vf_pool_uuid, flags); if (!SUCCESS(rc)) D_ERROR("Failed to destroy pool: " DF_RC "\n", DP_RC(rc)); - vos_self_fini(); +out_vf: + D_FREE(vf); +out: return rc; } diff --git a/src/utils/ddb/ddb_vos.h b/src/utils/ddb/ddb_vos.h index 68740540cf6..09e51382924 100644 --- a/src/utils/ddb/ddb_vos.h +++ b/src/utils/ddb/ddb_vos.h @@ -51,13 +51,26 @@ struct ddb_array { struct dv_indexed_tree_path *ddba_path; }; -/* Open and close a pool for a ddb_ctx */ +/** + * Open a VOS pool file. + * + * @param path VOS pool file path in the format "[/dir/]/(vos-N|rdb-pool)". + * @param db_path Path to the VOS metadata DB directory (SMD/NVMe). If NULL or empty, + * the DB directory is derived from the leading path component of path. + * @param poh Pool handle set on success. + * @param flags Flags forwarded to vos_pool_open() (e.g. VOS_POF_FOR_FEATURE_FLAG to + * skip VEA load when only reading/writing pool feature flags). + * @param write_mode When false the pool is mapped copy-on-write so that internal PMEMOBJ + * bookkeeping (SDS, ULOG replay) does not persist to the storage medium. + * @return 0 on success, negative DER error code otherwise. + */ +int +dv_pool_open(const char *path, const char *db_path, daos_handle_t *poh, uint32_t flags, + bool write_mode); int - dv_pool_open(const char *path, struct vos_file_parts *path_parts, daos_handle_t *poh, - uint32_t flags, bool write_mode); -int dv_pool_close(daos_handle_t poh); +dv_pool_close(daos_handle_t poh); int -dv_pool_destroy(const char *path, struct vos_file_parts *path_parts); +dv_pool_destroy(const char *path, const char *db_path, struct ddb_ctx *ctx); /* Update vos pool flags */ int diff --git a/src/utils/ddb/tests/ddb_commands_tests.c b/src/utils/ddb/tests/ddb_commands_tests.c index da90302ef3c..382d228be0a 100644 --- a/src/utils/ddb/tests/ddb_commands_tests.c +++ b/src/utils/ddb/tests/ddb_commands_tests.c @@ -89,7 +89,7 @@ ls_cmd_tests(void **state) /* printing a recx works */ dvt_fake_print_called = 0; - opt.path = "/[0]/[0]/[0]/[0]/[0]"; + opt.path = "/[0]/[0]/[0]/[1]/[0]"; opt.recursive = true; assert_success(ddb_run_ls(&ctx, &opt)); @@ -142,7 +142,7 @@ dump_value_cmd_tests(void **state) assert_rc_equal(ddb_run_value_dump(&ctx, &opt), -DDBER_INCOMPLETE_PATH_VALUE); /* Path is complete, no destination means will dump to screen */ - opt.path = "[0]/[0]/[0]/[1]"; + opt.path = "[0]/[0]/[0]/[2]"; assert_success(ddb_run_value_dump(&ctx, &opt)); /* success */ @@ -188,7 +188,7 @@ dump_ilog_cmd_tests(void **state) assert_rc_equal(ddb_run_ilog_dump(&ctx, &opt), -DER_INVAL); /* Dump akey ilog */ - opt.path = "[0]/[0]/[0]/[0]"; + opt.path = "[0]/[0]/[0]/[1]"; assert_success(ddb_run_ilog_dump(&ctx, &opt)); } @@ -211,8 +211,7 @@ dump_dtx_cmd_tests(void **state) { struct dt_vos_pool_ctx *tctx = *state; struct ddb_ctx ctx = {0}; - struct dtx_dump_options opt = {0}; - daos_handle_t coh; + struct dtx_dump_options opt = {0}; dvt_fake_print_reset(); @@ -222,11 +221,6 @@ dump_dtx_cmd_tests(void **state) assert_invalid(ddb_run_dtx_dump(&ctx, &opt)); - assert_success(vos_cont_open(tctx->dvt_poh, g_uuids[0], &coh)); - - dvt_vos_insert_2_records_with_dtx(coh); - vos_cont_close(coh); - opt.path = "[0]"; assert_success(ddb_run_dtx_dump(&ctx, &opt)); @@ -457,8 +451,7 @@ dtx_stat_tests(void **state) buf[59] += i; assert_regex_match(dvt_fake_print_buffer, buf); } - assert_regex_match(dvt_fake_print_buffer, - "^DTX entries statistics of the pool \\(null\\)$"); + assert_regex_match(dvt_fake_print_buffer, "^DTX entries statistics of the pool:$"); } static uint64_t @@ -567,18 +560,27 @@ dtx_aggr_tests(void **state) static int dcv_suit_setup(void **state) { + struct ddb_ctx ctx = {0}; struct dt_vos_pool_ctx *tctx; - struct vos_file_parts path_parts = {0}; + daos_handle_t coh; assert_success(ddb_test_setup_vos(state)); /* test setup creates the pool, but doesn't open it ... leave it open for these tests */ tctx = *state; - assert_success(parse_vos_file_parts(tctx->dvt_pmem_file, NULL, &path_parts)); - assert_success(dv_pool_open(tctx->dvt_pmem_file, &path_parts, &tctx->dvt_poh, 0, true)); - + ctx.dc_write_mode = true; + assert_success(dv_pool_open(tctx->dvt_pmem_file, NULL, &ctx.dc_poh, 0, ctx.dc_write_mode)); + tctx->dvt_poh = ctx.dc_poh; g_ctx.dc_poh = tctx->dvt_poh; + assert_success(vos_cont_open(ctx.dc_poh, g_uuids[0], &coh)); + + /* Seed the first container with 1 active + 1 committed DTX entry required by + * dtx_stat_tests, dtx_commit_entry_tests, dtx_act_discard_invalid_tests, and + * dtx_abort_entry_tests. */ + dvt_vos_insert_2_records_with_dtx(coh); + vos_cont_close(coh); + return 0; } diff --git a/src/utils/ddb/tests/ddb_parse_tests.c b/src/utils/ddb/tests/ddb_parse_tests.c index 76560a6826b..8392560c6a8 100644 --- a/src/utils/ddb/tests/ddb_parse_tests.c +++ b/src/utils/ddb/tests/ddb_parse_tests.c @@ -20,6 +20,7 @@ */ #define MOCKED_POOL_UUID_STR "12345678-1234-1234-1234-123456789012" +#define MOCKED_VOS_PATH_STR "/" MOCKED_POOL_UUID_STR "/vos-0" static int fake_print(const char *fmt, ...) @@ -74,16 +75,39 @@ parse_vos_file_parts_test_errors(void **state) rc = parse_vos_file_parts("/mnt/daos/" MOCKED_POOL_UUID_STR "/vos-01", NULL, &parts); assert_rc_equal(rc, -DER_INVAL); - /* Test invalid vos paths with too long db path */ + /* Test invalid VOS paths with too long VOS path */ + D_ALLOC_ARRAY_CHECK(buf, VOS_PATH_SIZE + 1); + memset(buf, 'a', VOS_PATH_SIZE + 1); + buf[0] = '/'; + memcpy(&buf[VOS_PATH_SIZE + 1 - sizeof(MOCKED_VOS_PATH_STR)], MOCKED_VOS_PATH_STR, + sizeof(MOCKED_VOS_PATH_STR)); + rc = parse_vos_file_parts(buf, NULL, &parts); + D_FREE(buf); + assert_rc_equal(rc, -DER_EXCEEDS_PATH_LEN); + + /* Test vos_path whose directory component exceeds DB_PATH_SIZE. + * Note: this does not directly test the DB_PATH_SIZE check since + * DB_PATH_SIZE == VOS_PATH_SIZE means the VOS_PATH_SIZE check fires first. + * This test remains relevant in case the two constants diverge in the future. */ D_ALLOC_ARRAY_CHECK(buf, DB_PATH_SIZE + 64); memset(buf, 'a', DB_PATH_SIZE + 64); buf[0] = '/'; - memcpy(&buf[DB_PATH_SIZE], "/" MOCKED_POOL_UUID_STR "/vos-0", - sizeof("/" MOCKED_POOL_UUID_STR "/vos-0")); + memcpy(&buf[DB_PATH_SIZE], MOCKED_VOS_PATH_STR, sizeof(MOCKED_VOS_PATH_STR)); rc = parse_vos_file_parts(buf, NULL, &parts); D_FREE(buf); assert_rc_equal(rc, -DER_EXCEEDS_PATH_LEN); + /* Test invalid db path in MD-on-SSD mode: in MD-on-SSD mode db_path is explicitly + * provided by the user, allowing the DB_PATH_SIZE check to be tested independently + * of the VOS_PATH_SIZE check. */ + D_ALLOC_ARRAY_CHECK(buf, DB_PATH_SIZE + 1); + memset(buf, 'a', DB_PATH_SIZE); + buf[0] = '/'; + buf[DB_PATH_SIZE] = '\0'; + rc = parse_vos_file_parts("/mnt/daos/" MOCKED_POOL_UUID_STR "/vos-0", buf, &parts); + D_FREE(buf); + assert_rc_equal(rc, -DER_EXCEEDS_PATH_LEN); + /* Test invalid vos paths with too long vos file name */ rc = parse_vos_file_parts("/mnt/daos/" MOCKED_POOL_UUID_STR "/vos-999999999999", NULL, &parts); @@ -103,15 +127,6 @@ parse_vos_file_parts_test_errors(void **state) rc = parse_vos_file_parts("/mnt/daos/" MOCKED_POOL_UUID_STR "/vos-99999999999", NULL, &parts); assert_rc_equal(rc, -DER_OVERFLOW); - - /* Test invalid vos paths with too long db path - MD-on-SSD */ - D_ALLOC_ARRAY_CHECK(buf, DB_PATH_SIZE + 1); - memset(buf, 'a', DB_PATH_SIZE); - buf[0] = '/'; - buf[DB_PATH_SIZE] = '\0'; - rc = parse_vos_file_parts("/mnt/daos/" MOCKED_POOL_UUID_STR "/vos-0", buf, &parts); - D_FREE(buf); - assert_rc_equal(rc, -DER_EXCEEDS_PATH_LEN); } static void @@ -125,8 +140,9 @@ parse_vos_file_parts_test_success(void **state) assert_rc_equal(rc, 0); /* Test with root path */ - rc = parse_vos_file_parts("/" MOCKED_POOL_UUID_STR "/vos-0", NULL, &parts); + rc = parse_vos_file_parts(MOCKED_VOS_PATH_STR, NULL, &parts); assert_rc_equal(rc, DER_SUCCESS); + assert_string_equal(MOCKED_VOS_PATH_STR, parts.vf_vos_file_path); assert_string_equal("/", parts.vf_db_path); assert_uuid_equal(expected_uuid, parts.vf_pool_uuid); assert_string_equal("vos-0", parts.vf_vos_file_name); @@ -135,6 +151,7 @@ parse_vos_file_parts_test_success(void **state) /* Test with absolute path */ rc = parse_vos_file_parts("/mnt/daos/" MOCKED_POOL_UUID_STR "/vos-0", NULL, &parts); assert_rc_equal(rc, DER_SUCCESS); + assert_string_equal("/mnt/daos/" MOCKED_POOL_UUID_STR "/vos-0", parts.vf_vos_file_path); assert_string_equal("/mnt/daos", parts.vf_db_path); assert_uuid_equal(expected_uuid, parts.vf_pool_uuid); assert_string_equal("vos-0", parts.vf_vos_file_name); @@ -144,6 +161,8 @@ parse_vos_file_parts_test_success(void **state) rc = parse_vos_file_parts("//////mnt////daos/////" MOCKED_POOL_UUID_STR "/////vos-0", NULL, &parts); assert_rc_equal(rc, DER_SUCCESS); + assert_string_equal("//////mnt////daos/////" MOCKED_POOL_UUID_STR "/////vos-0", + parts.vf_vos_file_path); assert_string_equal("//////mnt////daos", parts.vf_db_path); assert_uuid_equal(expected_uuid, parts.vf_pool_uuid); assert_string_equal("vos-0", parts.vf_vos_file_name); @@ -153,6 +172,7 @@ parse_vos_file_parts_test_success(void **state) memset(&parts, 0, sizeof(parts)); rc = parse_vos_file_parts("mnt/daos/" MOCKED_POOL_UUID_STR "/vos-42", NULL, &parts); assert_rc_equal(rc, DER_SUCCESS); + assert_string_equal("mnt/daos/" MOCKED_POOL_UUID_STR "/vos-42", parts.vf_vos_file_path); assert_string_equal("mnt/daos", parts.vf_db_path); assert_uuid_equal(expected_uuid, parts.vf_pool_uuid); assert_string_equal("vos-42", parts.vf_vos_file_name); @@ -161,6 +181,7 @@ parse_vos_file_parts_test_success(void **state) /* Test with relative path */ rc = parse_vos_file_parts("./" MOCKED_POOL_UUID_STR "/rdb-pool", NULL, &parts); assert_rc_equal(rc, DER_SUCCESS); + assert_string_equal("./" MOCKED_POOL_UUID_STR "/rdb-pool", parts.vf_vos_file_path); assert_string_equal(".", parts.vf_db_path); assert_uuid_equal(expected_uuid, parts.vf_pool_uuid); assert_string_equal("rdb-pool", parts.vf_vos_file_name); @@ -170,6 +191,7 @@ parse_vos_file_parts_test_success(void **state) memset(&parts, 1, sizeof(parts)); rc = parse_vos_file_parts(MOCKED_POOL_UUID_STR "/vos-909", NULL, &parts); assert_rc_equal(rc, DER_SUCCESS); + assert_string_equal(MOCKED_POOL_UUID_STR "/vos-909", parts.vf_vos_file_path); assert_string_equal(".", parts.vf_db_path); assert_uuid_equal(expected_uuid, parts.vf_pool_uuid); assert_string_equal("vos-909", parts.vf_vos_file_name); @@ -179,6 +201,7 @@ parse_vos_file_parts_test_success(void **state) rc = parse_vos_file_parts("/mnt/daos/" MOCKED_POOL_UUID_STR "/vos-0", "/my/db/path", &parts); assert_rc_equal(rc, DER_SUCCESS); + assert_string_equal("/mnt/daos/" MOCKED_POOL_UUID_STR "/vos-0", parts.vf_vos_file_path); assert_string_equal("/my/db/path", parts.vf_db_path); assert_uuid_equal(expected_uuid, parts.vf_pool_uuid); assert_string_equal("vos-0", parts.vf_vos_file_name); diff --git a/src/utils/ddb/tests/ddb_test_driver.c b/src/utils/ddb/tests/ddb_test_driver.c index 3cfcfc135b6..bbaa3ae8c5f 100644 --- a/src/utils/ddb/tests/ddb_test_driver.c +++ b/src/utils/ddb/tests/ddb_test_driver.c @@ -304,8 +304,8 @@ ddb_test_setup_vos(void **state) int ddb_teardown_vos(void **state) { - struct dt_vos_pool_ctx *tctx = *state; - struct vos_file_parts path_parts = {0}; + struct ddb_ctx ctx = {0}; + struct dt_vos_pool_ctx *tctx = *state; int rc = 0; if (tctx == NULL) { @@ -314,8 +314,7 @@ ddb_teardown_vos(void **state) } if (tctx->dvt_special_pool_destroy) { - assert_success(parse_vos_file_parts(tctx->dvt_pmem_file, NULL, &path_parts)); - rc = dv_pool_destroy(tctx->dvt_pmem_file, &path_parts); + rc = dv_pool_destroy(tctx->dvt_pmem_file, NULL, &ctx); } else { vos_self_init("/mnt/daos", false, 0); assert_success(vos_pool_destroy(tctx->dvt_pmem_file, tctx->dvt_pool_uuid)); diff --git a/src/utils/ddb/tests/ddb_vos_tests.c b/src/utils/ddb/tests/ddb_vos_tests.c index 74f76839b7d..52fabaa572b 100644 --- a/src/utils/ddb/tests/ddb_vos_tests.c +++ b/src/utils/ddb/tests/ddb_vos_tests.c @@ -182,18 +182,15 @@ __assert_ddb_iterate(daos_handle_t poh, uuid_t *cont_uuid, daos_unit_oid_t *oid, static void open_pool_test(void **state) { - daos_handle_t poh; - struct dt_vos_pool_ctx *tctx = *state; - struct vos_file_parts path_parts = {0}; - - assert_success(parse_vos_file_parts(tctx->dvt_pmem_file, NULL, &path_parts)); + struct ddb_ctx ctx = {0}; + struct dt_vos_pool_ctx *tctx = *state; - assert_success(dv_pool_open(tctx->dvt_pmem_file, &path_parts, &poh, 0, false)); - assert_success(dv_pool_close(poh)); + assert_success(dv_pool_open(tctx->dvt_pmem_file, NULL, &ctx.dc_poh, 0, ctx.dc_write_mode)); + assert_success(dv_pool_close(ctx.dc_poh)); /* should be able to open again after closing */ - assert_success(dv_pool_open(tctx->dvt_pmem_file, &path_parts, &poh, 0, false)); - assert_success(dv_pool_close(poh)); + assert_success(dv_pool_open(tctx->dvt_pmem_file, NULL, &ctx.dc_poh, 0, ctx.dc_write_mode)); + assert_success(dv_pool_close(ctx.dc_poh)); } static void @@ -1087,14 +1084,14 @@ dv_suit_teardown(void **state) static int dv_test_setup(void **state) { + struct ddb_ctx ctx = {0}; struct dt_vos_pool_ctx *tctx = *state; - struct vos_file_parts path_parts = {0}; - - assert_success(parse_vos_file_parts(tctx->dvt_pmem_file, NULL, &path_parts)); + ctx.dc_write_mode = true; active_entry_handler_called = 0; committed_entry_handler_called = 0; - assert_success(dv_pool_open(tctx->dvt_pmem_file, &path_parts, &tctx->dvt_poh, 0, true)); + assert_success(dv_pool_open(tctx->dvt_pmem_file, NULL, &ctx.dc_poh, 0, ctx.dc_write_mode)); + tctx->dvt_poh = ctx.dc_poh; return 0; } @@ -1110,24 +1107,23 @@ dv_test_teardown(void **state) static void pool_flags_tests(void **state) { - daos_handle_t poh; + struct ddb_ctx ctx = {0}; struct dt_vos_pool_ctx *tctx = *state; - struct vos_file_parts path_parts = {0}; uint64_t compat_flags; uint64_t incompat_flags; - assert_success(parse_vos_file_parts(tctx->dvt_pmem_file, NULL, &path_parts)); - assert_success( - dv_pool_open(tctx->dvt_pmem_file, &path_parts, &poh, VOS_POF_FOR_FEATURE_FLAG, true)); - assert_success(dv_pool_get_flags(poh, &compat_flags, &incompat_flags)); + ctx.dc_write_mode = true; + assert_success(dv_pool_open(tctx->dvt_pmem_file, NULL, &ctx.dc_poh, + VOS_POF_FOR_FEATURE_FLAG, ctx.dc_write_mode)); + assert_success(dv_pool_get_flags(ctx.dc_poh, &compat_flags, &incompat_flags)); assert(compat_flags == 0); assert(incompat_flags == 0); - assert_success( - dv_pool_update_flags(poh, VOS_POOL_COMPAT_FLAG_SUPP, VOS_POOL_INCOMPAT_FLAG_SUPP)); - assert_success(dv_pool_get_flags(poh, &compat_flags, &incompat_flags)); + assert_success(dv_pool_update_flags(ctx.dc_poh, VOS_POOL_COMPAT_FLAG_SUPP, + VOS_POOL_INCOMPAT_FLAG_SUPP)); + assert_success(dv_pool_get_flags(ctx.dc_poh, &compat_flags, &incompat_flags)); assert(compat_flags == VOS_POOL_COMPAT_FLAG_SUPP); assert(incompat_flags == VOS_POOL_INCOMPAT_FLAG_SUPP); - assert_success(dv_pool_close(poh)); + assert_success(dv_pool_close(ctx.dc_poh)); } #define SHA256_DIGEST_LEN 64 @@ -1176,15 +1172,16 @@ static void helper_stat_open_modify_close_stat(struct dt_vos_pool_ctx *tctx, struct file_state fs[2], bool write_mode) { - const char *path = tctx->dvt_pmem_file; - struct vos_file_parts path_parts = {0}; - daos_handle_t saved_poh = tctx->dvt_poh; + struct ddb_ctx ctx = {0}; + const char *path = tctx->dvt_pmem_file; + daos_handle_t saved_poh = tctx->dvt_poh; assert_int_equal(stat(path, &fs[FILE_STATE_PRE].stat), 0); sha256sum(path, fs[FILE_STATE_PRE].digest); - assert_success(parse_vos_file_parts(path, NULL, &path_parts)); - assert_success(dv_pool_open(path, &path_parts, &tctx->dvt_poh, 0, write_mode)); + ctx.dc_write_mode = write_mode; + assert_success(dv_pool_open(path, NULL, &ctx.dc_poh, 0, ctx.dc_write_mode)); + tctx->dvt_poh = ctx.dc_poh; update_value_to_modify_tests((void **)&tctx); assert_success(dv_pool_close(tctx->dvt_poh)); tctx->dvt_poh = saved_poh;