From db01e5b56fb37a4ff86778c3610585039fe7711f Mon Sep 17 00:00:00 2001 From: TorisutanKholwes Date: Fri, 23 Jan 2026 14:54:53 +0100 Subject: [PATCH 1/5] Improve postamble logic and enhance input field text color for better visibility --- core/tui/instance.go | 4 +++- core/tui/keys.go | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/core/tui/instance.go b/core/tui/instance.go index 0689ce372..47710b9ef 100644 --- a/core/tui/instance.go +++ b/core/tui/instance.go @@ -335,5 +335,7 @@ func (t *App) updateInstanceView() { table1.SetCell(i, 1, tview.NewTableCell(formatRel(instanceState.Monitor.Children)).SetSelectable(false)) } - postamble() + if focusTable, ok := t.app.GetFocus().(*tview.Table); !ok || focusTable.GetTitle() != table.GetTitle() { + postamble() + } } diff --git a/core/tui/keys.go b/core/tui/keys.go index 0c9edf391..7647d77a5 100644 --- a/core/tui/keys.go +++ b/core/tui/keys.go @@ -198,7 +198,8 @@ func (t *App) createAddInputBox() { } inputKeyName := tview.NewInputField().SetLabel("Key name:").SetFieldWidth(20). - SetFieldBackgroundColor(tcell.ColorDarkGray) + SetFieldBackgroundColor(tcell.ColorDarkGray). + SetFieldTextColor(tcell.ColorBlack) inputFlex := tview.NewFlex().SetDirection(tview.FlexColumn) inputFlex.AddItem(nil, 0, 1, false) From 814758a582480ca92727d755ec909f699f266c49 Mon Sep 17 00:00:00 2001 From: TorisutanKholwes Date: Fri, 23 Jan 2026 15:57:04 +0100 Subject: [PATCH 2/5] Use timeout on rdb command to avoid block in command and process --- daemon/nmon/main.go | 30 +++++++++++++++++++++++++++--- drivers/resdiskrados/main.go | 17 ++++++++++++++++- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/daemon/nmon/main.go b/daemon/nmon/main.go index 8f8667a0d..bda29e819 100644 --- a/daemon/nmon/main.go +++ b/daemon/nmon/main.go @@ -833,20 +833,44 @@ func (t *Manager) loadPools() { return } renewed := make(map[string]any) + + var renewedMu sync.Mutex + var wg sync.WaitGroup + renew := func(p pool.Pooler) { - ctx, cancel := context.WithTimeout(t.ctx, time.Minute) + defer wg.Done() + + ctx, cancel := context.WithTimeout(t.ctx, 10*time.Second) defer cancel() + poolName := p.Name() data := pool.GetStatus(ctx, p, true) + + if ctx.Err() != nil { + t.log.Warnf("loading pool '%s' status: %s", poolName, ctx.Err()) + return + } + t.log.Infof("pool '%s' status loaded", poolName) + + renewedMu.Lock() renewed[poolName] = nil + renewedMu.Unlock() + pool.StatusData.Set(poolName, t.localhost, data.DeepCopy()) t.publisher.Pub(&msgbus.NodePoolStatusUpdated{Node: t.localhost, Name: poolName, Value: data}, t.labelLocalhost) + } for _, p := range n.Pools() { - renew(p) + wg.Add(1) + go renew(p) } + + wg.Wait() for _, e := range pool.StatusData.GetByNode(t.localhost) { - if _, ok := renewed[e.Name]; !ok { + renewedMu.Lock() + _, ok := renewed[e.Name] + renewedMu.Unlock() + if !ok { pool.StatusData.Unset(e.Name, t.localhost) } } diff --git a/drivers/resdiskrados/main.go b/drivers/resdiskrados/main.go index 75db7b30e..26fa9337d 100644 --- a/drivers/resdiskrados/main.go +++ b/drivers/resdiskrados/main.go @@ -5,6 +5,9 @@ import ( "encoding/json" "fmt" "slices" + "time" + + "github.com/rs/zerolog" "github.com/opensvc/om3/v3/core/actionrollback" "github.com/opensvc/om3/v3/core/datarecv" @@ -17,7 +20,6 @@ import ( "github.com/opensvc/om3/v3/util/hostname" "github.com/opensvc/om3/v3/util/sizeconv" "github.com/opensvc/om3/v3/util/udevadm" - "github.com/rs/zerolog" ) type ( @@ -79,6 +81,10 @@ type ( } ) +const ( + DefaultCommandTimeout = 10 * time.Second +) + func New() resource.Driver { t := &T{} return t @@ -116,6 +122,7 @@ func (t *T) mapDevice(ctx context.Context) error { args = append(args, "map", t.Name) cmd := command.New( command.WithContext(ctx), + command.WithTimeout(DefaultCommandTimeout), command.WithName("rbd"), command.WithArgs(args), command.WithLogger(t.Log()), @@ -144,6 +151,7 @@ func (t *T) unmapDevice(ctx context.Context) error { args = append(args, "unmap", t.Name) cmd := command.New( command.WithContext(ctx), + command.WithTimeout(DefaultCommandTimeout), command.WithName("rbd"), command.WithArgs(args), command.WithLogger(t.Log()), @@ -166,6 +174,7 @@ func (t *T) createDevice(ctx context.Context) error { args = append(args, "create", "--size", fmt.Sprintf("%dB", bytes), t.Name) cmd := command.New( command.WithContext(ctx), + command.WithTimeout(DefaultCommandTimeout), command.WithName("rbd"), command.WithArgs(args), command.WithLogger(t.Log()), @@ -188,6 +197,7 @@ func (t *T) removeDevice(ctx context.Context) error { args = append(args, "remove", t.Name) cmd := command.New( command.WithContext(ctx), + command.WithTimeout(DefaultCommandTimeout), command.WithName("rbd"), command.WithArgs(args), command.WithLogger(t.Log()), @@ -217,6 +227,7 @@ func (t *T) lockDevice(ctx context.Context) error { } cmd := command.New( command.WithContext(ctx), + command.WithTimeout(DefaultCommandTimeout), command.WithName("rbd"), command.WithArgs(args), command.WithLogger(t.Log()), @@ -246,6 +257,7 @@ func (t *T) unlockDevice(ctx context.Context) error { } cmd := command.New( command.WithContext(ctx), + command.WithTimeout(DefaultCommandTimeout), command.WithName("rbd"), command.WithArgs(args), command.WithLogger(t.Log()), @@ -271,6 +283,7 @@ func (t *T) deviceInfo(ctx context.Context) (*RBDInfo, error) { args = append(args, "info", t.Name, "--format", "json") cmd := command.New( command.WithContext(ctx), + command.WithTimeout(DefaultCommandTimeout), command.WithName("rbd"), command.WithArgs(args), command.WithLogger(t.Log()), @@ -299,6 +312,7 @@ func (t *T) listLocks(ctx context.Context) ([]RBDLock, error) { args = append(args, "lock", "list", t.Name, "--format", "json") cmd := command.New( command.WithContext(ctx), + command.WithTimeout(DefaultCommandTimeout), command.WithName("rbd"), command.WithArgs(args), command.WithLogger(t.Log()), @@ -324,6 +338,7 @@ func (t *T) listDevices(ctx context.Context) ([]RBDMap, error) { args = append(args, "device", "list", "--format", "json") cmd := command.New( command.WithContext(ctx), + command.WithTimeout(DefaultCommandTimeout), command.WithName("rbd"), command.WithArgs(args), command.WithLogger(t.Log()), From e7becbcecccce478078bb6af05ffed1e9a49ebca Mon Sep 17 00:00:00 2001 From: TorisutanKholwes Date: Tue, 27 Jan 2026 10:40:49 +0100 Subject: [PATCH 3/5] Fix input focus bug and change name of inputs and color of head and input --- core/tui/instance.go | 2 +- core/tui/keys.go | 2 +- core/tui/main.go | 65 ++++++++++++++++++++++---------------------- core/tui/network.go | 2 +- core/tui/objects.go | 2 +- 5 files changed, 37 insertions(+), 36 deletions(-) diff --git a/core/tui/instance.go b/core/tui/instance.go index 47710b9ef..8893499f9 100644 --- a/core/tui/instance.go +++ b/core/tui/instance.go @@ -335,7 +335,7 @@ func (t *App) updateInstanceView() { table1.SetCell(i, 1, tview.NewTableCell(formatRel(instanceState.Monitor.Children)).SetSelectable(false)) } - if focusTable, ok := t.app.GetFocus().(*tview.Table); !ok || focusTable.GetTitle() != table.GetTitle() { + if focusTable, ok := t.app.GetFocus().(*tview.Table); !t.focused && (!ok || focusTable.GetTitle() != table.GetTitle()) { postamble() } } diff --git a/core/tui/keys.go b/core/tui/keys.go index 7647d77a5..1c4b23219 100644 --- a/core/tui/keys.go +++ b/core/tui/keys.go @@ -197,7 +197,7 @@ func (t *App) createAddInputBox() { clean() } - inputKeyName := tview.NewInputField().SetLabel("Key name:").SetFieldWidth(20). + inputKeyName := tview.NewInputField().SetLabel("Key name: ").SetFieldWidth(20). SetFieldBackgroundColor(tcell.ColorDarkGray). SetFieldTextColor(tcell.ColorBlack) diff --git a/core/tui/main.go b/core/tui/main.go index d6c31e99d..5d5354332 100644 --- a/core/tui/main.go +++ b/core/tui/main.go @@ -178,10 +178,9 @@ var ( colorNone = tcell.ColorNone colorSelected = tcell.ColorDarkSlateGray colorTitle = tcell.ColorGray - colorHead = tcell.ColorSteelBlue - colorHead2 = tcell.ColorOlive - colorHead3 = tcell.ColorCrimson - colorInput = tcell.ColorSteelBlue + colorHead = tcell.NewHexColor(0x315B7E) + colorHead2 = tcell.NewHexColor(0x386890) + colorHead3 = tcell.NewHexColor(0x23415A) colorHighlight = tcell.ColorWhite forceUpdate = true @@ -198,6 +197,7 @@ type Options struct { func Run(options *Options) error { app := NewApp(options) + os.Setenv("COLORTERM", "truecolor") if options != nil { if options.Selector != "" { app.Frame.Selector = options.Selector @@ -241,12 +241,10 @@ func (t *App) updateHead() { } } title := box.GetTitle() + t.head.SetCell(0, 0, tview.NewTableCell(conn()).SetBackgroundColor(colorHead3)) - t.head.SetCell(0, 1, tview.NewTableCell("◤").SetBackgroundColor(colorHead).SetTextColor(colorHead3)) - t.head.SetCell(0, 2, tview.NewTableCell(t.Frame.Current.Cluster.Config.Name).SetBackgroundColor(colorHead)) - t.head.SetCell(0, 3, tview.NewTableCell("◤").SetBackgroundColor(colorHead2).SetTextColor(colorHead)) - t.head.SetCell(0, 4, tview.NewTableCell(title).SetBackgroundColor(colorHead2)) - t.head.SetCell(0, 5, tview.NewTableCell("◤").SetTextColor(colorHead2)) + t.head.SetCell(0, 1, tview.NewTableCell(" "+t.Frame.Current.Cluster.Config.Name).SetBackgroundColor(colorHead)) + t.head.SetCell(0, 2, tview.NewTableCell(" "+title).SetBackgroundColor(colorHead2).SetExpansion(1)) } func (t viewId) String() string { @@ -871,6 +869,7 @@ func (t *App) onRuneColumn(event *tcell.EventKey) { clean := func() { t.flex.RemoveItem(t.command) t.command = nil + t.focused = false if !t.isOnConfirmation { t.app.SetFocus(t.flex.GetItem(1)) } @@ -1017,10 +1016,13 @@ func (t *App) onRuneColumn(event *tcell.EventKey) { t.errorf("unknown node action: %s", args[0]) } } + mainStyle := tcell.StyleDefault.Background(colorHead2).Foreground(tcell.ColorWhite) + selectedStyle := tcell.StyleDefault.Background(tcell.ColorWhite).Foreground(colorHead2) t.command = tview.NewInputField(). SetLabel(":"). SetFieldWidth(0). - SetFieldBackgroundColor(colorInput). + SetFieldBackgroundColor(colorHead3). + SetAutocompleteStyles(colorHead2, mainStyle, selectedStyle). SetAutocompleteFunc(func(currentText string) (entries []string) { completions := t.getCompletions(currentText) slices.Sort(completions) @@ -1044,15 +1046,15 @@ func (t *App) onRuneColumn(event *tcell.EventKey) { case "quit", "q": t.stop() case "connect": - t.nav(viewContext) clean() + t.nav(viewContext) case "filter": if len(args) < 2 { t.errorf("not enough arguments: filter ") return } - t.setFilter(args[1]) clean() + t.setFilter(args[1]) return case "go": if len(args) < 2 { @@ -1061,36 +1063,36 @@ func (t *App) onRuneColumn(event *tcell.EventKey) { } switch args[1] { case "sec": - t.setFilter("*/sec/*") clean() + t.setFilter("*/sec/*") return case "cfg": - t.setFilter("*/cfg/*") clean() + t.setFilter("*/cfg/*") return case "usr": - t.setFilter("*/usr/*") clean() + t.setFilter("*/usr/*") return case "svc": - t.setFilter("*/svc/*") clean() + t.setFilter("*/svc/*") return case "vol": - t.setFilter("*/vol/*") clean() + t.setFilter("*/vol/*") return case "pool": - t.nav(viewPool) clean() + t.nav(viewPool) return case "network", "net": - t.nav(viewNetwork) clean() + t.nav(viewNetwork) return case "relay": - t.nav(viewRelay) clean() + t.nav(viewRelay) return } case "do": @@ -1098,6 +1100,7 @@ func (t *App) onRuneColumn(event *tcell.EventKey) { t.errorf("not enough arguments: do ") return } + clean() switch { case len(t.selectedRIDs) > 0: resourceAction(args[1:], t.selectedRIDs) @@ -1138,7 +1141,6 @@ func (t *App) onRuneColumn(event *tcell.EventKey) { instanceAction(args[1:], selection) } } - clean() } case tcell.KeyEscape: clean() @@ -1147,6 +1149,7 @@ func (t *App) onRuneColumn(event *tcell.EventKey) { t.flex.RemoveItem(t.errs) t.flex.AddItem(t.command, 1, 0, true) t.app.SetFocus(t.command) + t.focused = true } func (t *App) setFilter(s string) { @@ -1301,13 +1304,9 @@ func (t *App) askInput(title string, onEnter func(inputValues ...string) bool, i centerFlex := tview.NewFlex().SetDirection(tview.FlexColumn) centerFlex.SetBackgroundColor(tcell.ColorBlack) - ratio := 1 - if len(inputData.label) > 10 { - ratio = 2 - } centerFlex.AddItem(filler, 0, 1, false) - centerFlex.AddItem(inputField, 0, 2*ratio, i == 0) - centerFlex.AddItem(filler, 0, 1*ratio, false) + centerFlex.AddItem(inputField, 0, 2, i == 0) + centerFlex.AddItem(filler, 0, 1, false) inputFlex.AddItem(centerFlex, 2, 0, i == 0) } @@ -2446,11 +2445,13 @@ func (t *App) createTable(creator CreateTableOptions) { v.Select(t.position.row, t.position.col) - t.flex.Clear() - t.flex.AddItem(t.head, 1, 0, false) - t.flex.AddItem(v, 0, 1, true) - t.app.SetFocus(v) - t.updateHead() + if focusTable, ok := t.app.GetFocus().(*tview.Table); !t.focused && (!ok || focusTable.GetTitle() != v.GetTitle()) { + t.flex.Clear() + t.flex.AddItem(t.head, 1, 0, false) + t.flex.AddItem(v, 0, 1, true) + t.app.SetFocus(v) + t.updateHead() + } } func (t *App) selectedString() string { diff --git a/core/tui/network.go b/core/tui/network.go index c33388052..ad271a60e 100644 --- a/core/tui/network.go +++ b/core/tui/network.go @@ -16,7 +16,7 @@ import ( ) func (t *App) updateNetworkList() { - title := "Networks" + title := "networks" titles := []string{"NAME", "TYPE", "NETWORK", "SIZE", "USED", "FREE"} var elementsList [][]string diff --git a/core/tui/objects.go b/core/tui/objects.go index 29ae1ccaf..122dc9b32 100644 --- a/core/tui/objects.go +++ b/core/tui/objects.go @@ -297,7 +297,7 @@ func (t *App) updateObjects() { t.lastDraw = time.Now() t.objects.Clear() - t.objects.SetTitle(fmt.Sprintf("%s objects", t.Frame.Selector)) + t.objects.SetTitle(fmt.Sprintf("objects matching %s", t.Frame.Selector)) for i, obj := range objects { row := i From 2b3bef6738683c2c701c645d02fff369555b55b9 Mon Sep 17 00:00:00 2001 From: TorisutanKholwes Date: Tue, 27 Jan 2026 16:16:54 +0100 Subject: [PATCH 4/5] Add DefaultQueryTimeout on rados disk driver --- drivers/resdiskrados/main.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/resdiskrados/main.go b/drivers/resdiskrados/main.go index 26fa9337d..1fe10708b 100644 --- a/drivers/resdiskrados/main.go +++ b/drivers/resdiskrados/main.go @@ -82,7 +82,8 @@ type ( ) const ( - DefaultCommandTimeout = 10 * time.Second + DefaultCommandTimeout = 30 * time.Second + DefaultQueryTimeout = 10 * time.Second ) func New() resource.Driver { @@ -283,7 +284,7 @@ func (t *T) deviceInfo(ctx context.Context) (*RBDInfo, error) { args = append(args, "info", t.Name, "--format", "json") cmd := command.New( command.WithContext(ctx), - command.WithTimeout(DefaultCommandTimeout), + command.WithTimeout(DefaultQueryTimeout), command.WithName("rbd"), command.WithArgs(args), command.WithLogger(t.Log()), @@ -312,7 +313,7 @@ func (t *T) listLocks(ctx context.Context) ([]RBDLock, error) { args = append(args, "lock", "list", t.Name, "--format", "json") cmd := command.New( command.WithContext(ctx), - command.WithTimeout(DefaultCommandTimeout), + command.WithTimeout(DefaultQueryTimeout), command.WithName("rbd"), command.WithArgs(args), command.WithLogger(t.Log()), @@ -338,7 +339,7 @@ func (t *T) listDevices(ctx context.Context) ([]RBDMap, error) { args = append(args, "device", "list", "--format", "json") cmd := command.New( command.WithContext(ctx), - command.WithTimeout(DefaultCommandTimeout), + command.WithTimeout(DefaultQueryTimeout), command.WithName("rbd"), command.WithArgs(args), command.WithLogger(t.Log()), @@ -538,6 +539,7 @@ func (t *T) disableFeature(ctx context.Context, feature string) error { args = append(args, "feature", "disable", t.Name, feature) cmd := command.New( command.WithContext(ctx), + command.WithTimeout(DefaultCommandTimeout), command.WithName("rbd"), command.WithArgs(args), command.WithLogger(t.Log()), @@ -556,6 +558,7 @@ func (t *T) enableFeature(ctx context.Context, feature string) error { args = append(args, "feature", "enable", t.Name, feature) cmd := command.New( command.WithContext(ctx), + command.WithTimeout(DefaultCommandTimeout), command.WithName("rbd"), command.WithArgs(args), command.WithLogger(t.Log()), From 3ce2e8e6bead490ffb21218c7eb2327022903dc2 Mon Sep 17 00:00:00 2001 From: TorisutanKholwes Date: Tue, 27 Jan 2026 17:19:24 +0100 Subject: [PATCH 5/5] add more explicit error message on rbd commands --- drivers/resdiskrados/main.go | 43 +++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/drivers/resdiskrados/main.go b/drivers/resdiskrados/main.go index 1fe10708b..4a889489a 100644 --- a/drivers/resdiskrados/main.go +++ b/drivers/resdiskrados/main.go @@ -131,7 +131,10 @@ func (t *T) mapDevice(ctx context.Context) error { command.WithStdoutLogLevel(zerolog.InfoLevel), command.WithStderrLogLevel(zerolog.ErrorLevel), ) - return cmd.Run() + if err := cmd.Run(); err != nil { + return fmt.Errorf("map: %v", err) + } + return nil } func (t *T) unmapDevice(ctx context.Context) error { @@ -160,7 +163,10 @@ func (t *T) unmapDevice(ctx context.Context) error { command.WithStdoutLogLevel(zerolog.InfoLevel), command.WithStderrLogLevel(zerolog.ErrorLevel), ) - return cmd.Run() + if err := cmd.Run(); err != nil { + return fmt.Errorf("unmap: %v", err) + } + return nil } func (t *T) createDevice(ctx context.Context) error { @@ -184,7 +190,7 @@ func (t *T) createDevice(ctx context.Context) error { command.WithStderrLogLevel(zerolog.ErrorLevel), ) if err := cmd.Run(); err != nil { - return err + return fmt.Errorf("create: %v", err) } udevadm.Settle() return nil @@ -206,7 +212,10 @@ func (t *T) removeDevice(ctx context.Context) error { command.WithStdoutLogLevel(zerolog.InfoLevel), command.WithStderrLogLevel(zerolog.ErrorLevel), ) - return cmd.Run() + if err := cmd.Run(); err != nil { + return fmt.Errorf("remove: %v", err) + } + return nil } func (t *T) lockDevice(ctx context.Context) error { @@ -236,7 +245,10 @@ func (t *T) lockDevice(ctx context.Context) error { command.WithStdoutLogLevel(zerolog.InfoLevel), command.WithStderrLogLevel(zerolog.ErrorLevel), ) - return cmd.Run() + if err := cmd.Run(); err != nil { + return fmt.Errorf("lock: %v", err) + } + return nil } func (t *T) unlockDevice(ctx context.Context) error { @@ -266,7 +278,10 @@ func (t *T) unlockDevice(ctx context.Context) error { command.WithStdoutLogLevel(zerolog.InfoLevel), command.WithStderrLogLevel(zerolog.ErrorLevel), ) - return cmd.Run() + if err := cmd.Run(); err != nil { + return fmt.Errorf("unlock: %v", err) + } + return nil } func (t *T) Info(ctx context.Context) (resource.InfoKeys, error) { @@ -296,7 +311,7 @@ func (t *T) deviceInfo(ctx context.Context) (*RBDInfo, error) { return nil, nil } if err != nil { - return nil, err + return nil, fmt.Errorf("info: %v", err) } var data RBDInfo if err := json.Unmarshal(b, &data); err != nil { @@ -322,7 +337,7 @@ func (t *T) listLocks(ctx context.Context) ([]RBDLock, error) { ) b, err := cmd.Output() if err != nil { - return nil, err + return nil, fmt.Errorf("lock list: %v", err) } var data []RBDLock if err := json.Unmarshal(b, &data); err != nil { @@ -348,7 +363,7 @@ func (t *T) listDevices(ctx context.Context) ([]RBDMap, error) { ) b, err := cmd.Output() if err != nil { - return nil, err + return nil, fmt.Errorf("device list: %v", err) } var data []RBDMap if err := json.Unmarshal(b, &data); err != nil { @@ -547,7 +562,10 @@ func (t *T) disableFeature(ctx context.Context, feature string) error { command.WithStdoutLogLevel(zerolog.InfoLevel), command.WithStderrLogLevel(zerolog.ErrorLevel), ) - return cmd.Run() + if err := cmd.Run(); err != nil { + return fmt.Errorf("feature disable: %v", err) + } + return nil } func (t *T) enableFeature(ctx context.Context, feature string) error { @@ -566,7 +584,10 @@ func (t *T) enableFeature(ctx context.Context, feature string) error { command.WithStdoutLogLevel(zerolog.InfoLevel), command.WithStderrLogLevel(zerolog.ErrorLevel), ) - return cmd.Run() + if err := cmd.Run(); err != nil { + return fmt.Errorf("feature enable: %v", err) + } + return nil }