diff --git a/pkg/context/context.go b/pkg/context/context.go index fb8de528..b15a6b66 100644 --- a/pkg/context/context.go +++ b/pkg/context/context.go @@ -24,7 +24,10 @@ type Context struct { PathOverrides option.PathOverrides snapshotUnpackedPath string alert option.Alerter - err error + Err error + DisableNodeCaches bool + DisableNodeAreas bool + DisableNodeDistances bool } // WithContext returns an option.Option that contains a pre-existing Context @@ -83,12 +86,18 @@ func New(opts ...*option.Option) *Context { ctx.PathOverrides = merged.PathOverrides } + if merged.TopologyOptions != nil { + ctx.DisableNodeCaches = merged.TopologyOptions.DisableNodeCaches + ctx.DisableNodeAreas = merged.TopologyOptions.DisableNodeAreas + ctx.DisableNodeDistances = merged.TopologyOptions.DisableNodeDistances + } + // New is not allowed to return error - it would break the established API. // so the only way out is to actually do the checks here and record the error, // and return it later, at the earliest possible occasion, in Setup() if ctx.SnapshotPath != "" && ctx.Chroot != option.DefaultChroot { // The env/client code supplied a value, but we are will overwrite it when unpacking shapshots! - ctx.err = fmt.Errorf("Conflicting options: chroot %q and snapshot path %q", ctx.Chroot, ctx.SnapshotPath) + ctx.Err = fmt.Errorf("Conflicting options: chroot %q and snapshot path %q", ctx.Chroot, ctx.SnapshotPath) } return ctx } @@ -102,11 +111,14 @@ func FromEnv() *Context { snapRootVal := option.EnvOrDefaultSnapshotRoot() snapExclusiveVal := option.EnvOrDefaultSnapshotExclusive() return &Context{ - Chroot: chrootVal, - EnableTools: enableTools, - SnapshotPath: snapPathVal, - SnapshotRoot: snapRootVal, - SnapshotExclusive: snapExclusiveVal, + Chroot: chrootVal, + EnableTools: enableTools, + SnapshotPath: snapPathVal, + SnapshotRoot: snapRootVal, + SnapshotExclusive: snapExclusiveVal, + DisableNodeCaches: option.EnvOrDefaultDisableNodeCaches(), + DisableNodeAreas: option.EnvOrDefaultDisableNodeAreas(), + DisableNodeDistances: option.EnvOrDefaultDisableNodeDistances(), } } @@ -131,8 +143,8 @@ func (ctx *Context) Do(fn func() error) error { // You should call `Setup` just once. It is safe to call `Setup` if you don't make // use of optional extra features - `Setup` will do nothing. func (ctx *Context) Setup() error { - if ctx.err != nil { - return ctx.err + if ctx.Err != nil { + return ctx.Err } if ctx.SnapshotPath == "" { // nothing to do! diff --git a/pkg/memory/memory_cache_linux.go b/pkg/memory/memory_cache_linux.go index 12258ea4..dc68e374 100644 --- a/pkg/memory/memory_cache_linux.go +++ b/pkg/memory/memory_cache_linux.go @@ -20,6 +20,10 @@ import ( ) func CachesForNode(ctx *context.Context, nodeID int) ([]*Cache, error) { + if ctx.DisableNodeCaches { + ctx.Warn("Node caches disabled. Returning empty cache list.") + return nil, nil + } // The /sys/devices/node/nodeX directory contains a subdirectory called // 'cpuX' for each logical processor assigned to the node. Each of those // subdirectories containers a 'cache' subdirectory which contains a number diff --git a/pkg/memory/memory_linux.go b/pkg/memory/memory_linux.go index d5a54101..d74ff414 100644 --- a/pkg/memory/memory_linux.go +++ b/pkg/memory/memory_linux.go @@ -61,6 +61,10 @@ func (i *Info) load() error { } func AreaForNode(ctx *context.Context, nodeID int) (*Area, error) { + if ctx.DisableNodeAreas { + ctx.Warn("Node areas disabled. Returning nil area.") + return nil, nil + } paths := linuxpath.New(ctx) path := filepath.Join( paths.SysDevicesSystemNode, diff --git a/pkg/option/option.go b/pkg/option/option.go index 7ce14016..e2fb89f4 100644 --- a/pkg/option/option.go +++ b/pkg/option/option.go @@ -17,13 +17,16 @@ const ( ) const ( - envKeyChroot = "GHW_CHROOT" - envKeyDisableWarnings = "GHW_DISABLE_WARNINGS" - envKeyDisableTools = "GHW_DISABLE_TOOLS" - envKeySnapshotPath = "GHW_SNAPSHOT_PATH" - envKeySnapshotRoot = "GHW_SNAPSHOT_ROOT" - envKeySnapshotExclusive = "GHW_SNAPSHOT_EXCLUSIVE" - envKeySnapshotPreserve = "GHW_SNAPSHOT_PRESERVE" + envKeyChroot = "GHW_CHROOT" + envKeyDisableWarnings = "GHW_DISABLE_WARNINGS" + envKeyDisableTools = "GHW_DISABLE_TOOLS" + envKeySnapshotPath = "GHW_SNAPSHOT_PATH" + envKeySnapshotRoot = "GHW_SNAPSHOT_ROOT" + envKeySnapshotExclusive = "GHW_SNAPSHOT_EXCLUSIVE" + envKeySnapshotPreserve = "GHW_SNAPSHOT_PRESERVE" + envKeyDisableNodeCaches = "GHW_DISABLE_NODE_CACHES" + envKeyDisableNodeAreas = "GHW_DISABLE_NODE_AREAS" + envKeyDisableNodeDistances = "GHW_DISABLE_NODE_DISTANCES" ) // Alerter emits warnings about undesirable but recoverable errors. @@ -140,6 +143,9 @@ type Option struct { // during a call to the `context.WithContext` function. Only used internally. // This is an interface to get around recursive package import issues. Context interface{} + + // TopologyOptions contains options for handling of ghw topology + TopologyOptions *TopologyOptions } // SnapshotOptions contains options for handling of ghw snapshots @@ -163,6 +169,16 @@ type SnapshotOptions struct { Exclusive bool } +// TopologyOptions contains options for handling of ghw topology +type TopologyOptions struct { + // DisableNodeCaches disables the collection of node caches + DisableNodeCaches bool + // DisableNodeAreas disables the collection of node memory areas + DisableNodeAreas bool + // DisableNodeDistances disables the collection of node distances + DisableNodeDistances bool +} + // WithChroot allows to override the root directory ghw uses. func WithChroot(dir string) *Option { return &Option{Chroot: &dir} @@ -233,6 +249,9 @@ func Merge(opts ...*Option) *Option { if opt.Context != nil { merged.Context = opt.Context } + if opt.TopologyOptions != nil { + merged.TopologyOptions = opt.TopologyOptions + } } // Set the default value if missing from mergeOpts if merged.Chroot == nil { @@ -254,5 +273,37 @@ func Merge(opts ...*Option) *Option { enabled := EnvOrDefaultTools() merged.EnableTools = &enabled } + if merged.TopologyOptions == nil { + merged.TopologyOptions = &TopologyOptions{ + DisableNodeCaches: EnvOrDefaultDisableNodeCaches(), + DisableNodeAreas: EnvOrDefaultDisableNodeAreas(), + DisableNodeDistances: EnvOrDefaultDisableNodeDistances(), + } + } + return merged } + +// EnvOrDefaultDisableNodeCaches ... +func EnvOrDefaultDisableNodeCaches() bool { + if _, exists := os.LookupEnv(envKeyDisableNodeCaches); exists { + return true + } + return false +} + +// EnvOrDefaultDisableNodeCaches ... +func EnvOrDefaultDisableNodeAreas() bool { + if _, exists := os.LookupEnv(envKeyDisableNodeAreas); exists { + return true + } + return false +} + +// EnvOrDefaultDisableNodeDistances ... +func EnvOrDefaultDisableNodeDistances() bool { + if _, exists := os.LookupEnv(envKeyDisableNodeDistances); exists { + return true + } + return false +} diff --git a/pkg/topology/topology_linux.go b/pkg/topology/topology_linux.go index 9d8434cb..296348bb 100644 --- a/pkg/topology/topology_linux.go +++ b/pkg/topology/topology_linux.go @@ -46,18 +46,21 @@ func topologyNodes(ctx *context.Context) []*Node { nodeID, err := strconv.Atoi(filename[4:]) if err != nil { ctx.Warn("failed to determine node ID: %s\n", err) + ctx.Err = err return nodes } node.ID = nodeID cores, err := cpu.CoresForNode(ctx, nodeID) if err != nil { ctx.Warn("failed to determine cores for node: %s\n", err) + ctx.Err = err return nodes } node.Cores = cores caches, err := memory.CachesForNode(ctx, nodeID) if err != nil { ctx.Warn("failed to determine caches for node: %s\n", err) + ctx.Err = err return nodes } node.Caches = caches @@ -65,6 +68,7 @@ func topologyNodes(ctx *context.Context) []*Node { distances, err := distancesForNode(ctx, nodeID) if err != nil { ctx.Warn("failed to determine node distances for node: %s\n", err) + ctx.Err = err return nodes } node.Distances = distances @@ -72,6 +76,7 @@ func topologyNodes(ctx *context.Context) []*Node { area, err := memory.AreaForNode(ctx, nodeID) if err != nil { ctx.Warn("failed to determine memory area for node: %s\n", err) + ctx.Err = err return nodes } node.Memory = area @@ -82,6 +87,10 @@ func topologyNodes(ctx *context.Context) []*Node { } func distancesForNode(ctx *context.Context, nodeID int) ([]int, error) { + if ctx.DisableNodeDistances { + ctx.Warn("Node distances disabled. Returning empty distances list.") + return nil, nil + } paths := linuxpath.New(ctx) path := filepath.Join( paths.SysDevicesSystemNode,