Utku Ozdemir 9e07832db9
feat: implement summary dashboard
Implement the new summary dashboard with node info and logs.
Replace the previous metrics dashboard with the new dashboard which has multiple screens for node summary, metrics and editing network config.

Port the old metrics dashboard to the tview library and assign it to be a screen in the new dashboard, accessible by F2 key.

Add a new resource, infos.cluster.talos.dev which contains the cluster name and id of a node.

Disable the network config editor screen in the new dashboard until it is fully implemented with its backend.

Closes siderolabs/talos#4790.

Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
2023-03-15 13:13:28 +01:00

174 lines
4.5 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package components
import (
"fmt"
"github.com/dustin/go-humanize"
"github.com/rivo/tview"
"github.com/siderolabs/talos/internal/pkg/dashboard/data"
)
// LoadAvgInfo represents the widget with load average info.
type LoadAvgInfo struct {
tview.TextView
}
// NewLoadAvgInfo initializes LoadAvgInfo.
func NewLoadAvgInfo() *LoadAvgInfo {
widget := &LoadAvgInfo{
TextView: *tview.NewTextView(),
}
widget.SetBorder(false)
widget.SetBorderPadding(1, 0, 1, 0)
widget.SetDynamicColors(true)
return widget
}
// Update implements the DataWidget interface.
func (widget *LoadAvgInfo) Update(node string, data *data.Data) {
nodeData := data.Nodes[node]
if nodeData == nil {
widget.SetText(noData)
} else {
widget.SetText(fmt.Sprintf(
"[::b]LOAD[::-]\n"+
"1 min [::b]%6.2f[::-]\n"+
"5 min [::b]%6.2f[::-]\n"+
"15 min [::b]%6.2f[::-]",
nodeData.LoadAvg.GetLoad1(),
nodeData.LoadAvg.GetLoad5(),
nodeData.LoadAvg.GetLoad15(),
))
}
}
// ProcsInfo represents the widget with processes info.
type ProcsInfo struct {
tview.TextView
}
// NewProcsInfo initializes ProcsInfo.
func NewProcsInfo() *ProcsInfo {
widget := &ProcsInfo{
TextView: *tview.NewTextView(),
}
widget.SetBorder(false)
widget.SetBorderPadding(1, 0, 1, 0)
widget.SetDynamicColors(true)
return widget
}
// Update implements the DataWidget interface.
func (widget *ProcsInfo) Update(node string, data *data.Data) {
nodeData := data.Nodes[node]
if nodeData == nil {
widget.SetText(noData)
} else {
procsCreated, suffix := humanize.ComputeSI(float64(nodeData.ProcsCreated()))
widget.SetText(fmt.Sprintf(
"[::b]PROCS[::-]\n"+
"Created [::b]%5.1f%s[::-]\n"+
"Running [::b]%5d[::-]\n"+
"Blocked [::b]%5d[::-]",
procsCreated, suffix,
nodeData.SystemStat.GetProcessRunning(),
nodeData.SystemStat.GetProcessBlocked(),
))
}
}
// MemInfo represents the widget with memory info.
type MemInfo struct {
tview.TextView
}
// NewMemInfo initializes LoadAvgInfo.
func NewMemInfo() *MemInfo {
widget := &MemInfo{
TextView: *tview.NewTextView(),
}
widget.SetBorder(false)
widget.SetBorderPadding(1, 0, 1, 0)
widget.SetDynamicColors(true)
return widget
}
// Update implements the DataWidget interface.
func (widget *MemInfo) Update(node string, data *data.Data) {
nodeData := data.Nodes[node]
if nodeData == nil {
widget.SetText(noData)
} else {
widget.SetText(fmt.Sprintf(
"[::b]MEMORY[::-]\n"+
"Total [::b]%8s[::-] Buffers [::b]%8s[::-]\n"+
"Used [::b]%8s[::-] Cache [::b]%8s[::-]\n"+
"Free [::b]%8s[::-] Avail [::b]%8s[::-]\n"+
"Shared [::b]%8s[::-]\n",
humanize.Bytes(nodeData.Memory.GetMeminfo().GetMemtotal()<<10),
humanize.Bytes(nodeData.Memory.GetMeminfo().GetBuffers()<<10),
humanize.Bytes((nodeData.Memory.GetMeminfo().GetMemtotal()-nodeData.Memory.GetMeminfo().GetMemfree()-nodeData.Memory.GetMeminfo().GetCached()-nodeData.Memory.GetMeminfo().GetBuffers())<<10),
humanize.Bytes(nodeData.Memory.GetMeminfo().GetCached()<<10),
humanize.Bytes(nodeData.Memory.GetMeminfo().GetMemfree()<<10),
humanize.Bytes(nodeData.Memory.GetMeminfo().GetMemavailable()<<10),
humanize.Bytes(nodeData.Memory.GetMeminfo().GetShmem()<<10),
))
}
}
// CPUInfo represents the widget with CPU info.
type CPUInfo struct {
tview.TextView
}
// NewCPUInfo initializes CPUInfo.
func NewCPUInfo() *CPUInfo {
widget := &CPUInfo{
TextView: *tview.NewTextView(),
}
widget.SetBorder(false)
widget.SetBorderPadding(1, 0, 1, 0)
widget.SetDynamicColors(true)
return widget
}
// Update implements the DataWidget interface.
func (widget *CPUInfo) Update(node string, data *data.Data) {
nodeData := data.Nodes[node]
if nodeData == nil {
widget.SetText(noData)
} else {
ctxSw, suffix := humanize.ComputeSI(float64(nodeData.CtxSwitches()))
widget.SetText(fmt.Sprintf(
"[::b]CPU[::-]\n"+
"User [::b]%5.1f%%[::-] Nice [::b]%5.1f%%[::-]\n"+
"System [::b]%5.1f%%[::-] IRQ [::b]%5.1f%%[::-]\n"+
"Idle [::b]%5.1f%%[::-] Iowait [::b]%5.1f%%[::-]\n"+
"Steal [::b]%5.1f%%[::-] CtxSw [::b]%5.1f%s[::-]\n",
nodeData.CPUUsageByName("user")*100.0, nodeData.CPUUsageByName("nice")*100.0,
nodeData.CPUUsageByName("system")*100.0, nodeData.CPUUsageByName("irq")*100.0,
nodeData.CPUUsageByName("idle")*100.0, nodeData.CPUUsageByName("iowait")*100.0,
nodeData.CPUUsageByName("steal")*100.0, ctxSw, suffix,
))
}
}