diff --git a/core/tui/instance.go b/core/tui/instance.go index 0689ce372..8893499f9 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); !t.focused && (!ok || focusTable.GetTitle() != table.GetTitle()) { + postamble() + } } diff --git a/core/tui/keys.go b/core/tui/keys.go index 0c9edf391..1c4b23219 100644 --- a/core/tui/keys.go +++ b/core/tui/keys.go @@ -197,8 +197,9 @@ func (t *App) createAddInputBox() { clean() } - inputKeyName := tview.NewInputField().SetLabel("Key name:").SetFieldWidth(20). - SetFieldBackgroundColor(tcell.ColorDarkGray) + inputKeyName := tview.NewInputField().SetLabel("Key name: ").SetFieldWidth(20). + SetFieldBackgroundColor(tcell.ColorDarkGray). + SetFieldTextColor(tcell.ColorBlack) inputFlex := tview.NewFlex().SetDirection(tview.FlexColumn) inputFlex.AddItem(nil, 0, 1, false) 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 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..4a889489a 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,11 @@ type ( } ) +const ( + DefaultCommandTimeout = 30 * time.Second + DefaultQueryTimeout = 10 * time.Second +) + func New() resource.Driver { t := &T{} return t @@ -116,6 +123,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()), @@ -123,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 { @@ -144,6 +155,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()), @@ -151,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 { @@ -166,6 +181,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()), @@ -174,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 @@ -188,6 +204,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()), @@ -195,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 { @@ -217,6 +237,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()), @@ -224,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 { @@ -246,6 +270,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()), @@ -253,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) { @@ -271,6 +299,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(DefaultQueryTimeout), command.WithName("rbd"), command.WithArgs(args), command.WithLogger(t.Log()), @@ -282,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 { @@ -299,6 +328,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(DefaultQueryTimeout), command.WithName("rbd"), command.WithArgs(args), command.WithLogger(t.Log()), @@ -307,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 { @@ -324,6 +354,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(DefaultQueryTimeout), command.WithName("rbd"), command.WithArgs(args), command.WithLogger(t.Log()), @@ -332,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 { @@ -523,6 +554,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()), @@ -530,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 { @@ -541,6 +576,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()), @@ -548,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 }