package main import ( "fmt" "sort" "strings" "time" "github.com/novox/mesh-controller/internal/inventory" ) // machineLines is what a machine said it is made of (novox/hq issue 160), as `node` shows it: every fact in // one block with its age, the architecture and kernel the profile always carried among them, and each fact // the machine could not determine said as unknown with its why — never as a zero, and never left out. func machineLines(a inventory.MachineAccount, had bool, now time.Time) []string { if !had { return []string{" what it is made of: never said — this machine has not reported a profile"} } m := a.Machine if m == nil { return []string{ " what it is made of: not said — its node-engine is older than the one that says it", " " + factLine("architecture", orUnsaid(a.Architecture)), " " + factLine("kernel", orUnsaid(a.Kernel)), } } age := roughly(now.Sub(m.ObservedAt)) + " ago" if m.ObservedAt.IsZero() { // Said with no time: its age is not known, and is never read as two thousand years. age = "at a time it did not say (age unknown)" } out := []string{fmt.Sprintf(" what it is made of, as it said %s:", age)} add := func(name, value string) { out = append(out, " "+factLine(name, value)) } unknown := map[string]string{} for k, v := range m.Unknown { unknown[k] = v } // said is the value, or the fact's own reason it is unknown; a fact neither said nor unknown is a // node-engine that broke its own rule, and is said so rather than shown blank. said := func(fact, value string) string { if value != "" { delete(unknown, fact) return value } if why, ok := unknown[fact]; ok { delete(unknown, fact) return "unknown: " + why } return "unknown: the machine said neither it nor why" } add("architecture", orUnsaid(a.Architecture)) release := said("kernel release", m.KernelRelease) if m.KernelRelease == "" { release = "(release " + release + ")" } add("kernel", orUnsaid(a.Kernel)+" "+release) dist := said("distribution", m.Distribution) if m.Distribution != "" { dist += " " + said("version", m.Version) } add("distribution", dist) add("processor", said("processor", m.Processor)) add("cores", said("cores", countWords(m.Cores))) add("threads", said("threads", countWords(m.Threads))) add("memory", said("memory", bytesWords(m.MemoryBytes))) add("virtual", said("virtual or physical", m.Virtual)) up := "" if m.BootedAt != nil { up = fmt.Sprintf("%s, since %s", roughly(now.Sub(*m.BootedAt)), m.BootedAt.Local().Format("2006-01-02 15:04")) } add("up", said("uptime", up)) add("timezone", said("timezone", m.Timezone)) if len(m.Filesystems) == 0 { add("filesystems", said("filesystems", "")) } else { for i, f := range m.Filesystems { name := "" if i == 0 { name = "filesystems" } add(name, fmt.Sprintf("%s %s, %s free (%s %s)", f.Mount, bytesWords(f.SizeBytes), bytesWords(f.FreeBytes), f.Type, f.Device)) } if why, ok := unknown["filesystems"]; ok { add("", "and some unknown: "+why) delete(unknown, "filesystems") } } add("public address", said("public address", "")) // Any other fact a newer node-engine named unknown, so nothing it said is dropped. rest := make([]string, 0, len(unknown)) for k := range unknown { rest = append(rest, k) } sort.Strings(rest) for _, k := range rest { add(k, "unknown: "+unknown[k]) } return out } func factLine(name, value string) string { return fmt.Sprintf("%-15s %s", name, value) } func orUnsaid(s string) string { if strings.TrimSpace(s) == "" { return "not said" } return s } // countWords is a number a machine said, or nothing when it said none (its unknown says why). func countWords(n int) string { if n <= 0 { return "" } return fmt.Sprint(n) } // bytesWords is an amount of bytes a person reads, in binary units; nothing when none was said. func bytesWords(n int64) string { if n <= 0 { return "" } units := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB"} v, u := float64(n), 0 for v >= 1024 && u < len(units)-1 { v /= 1024 u++ } if u == 0 { return fmt.Sprintf("%d B", n) } return fmt.Sprintf("%.1f %s", v, units[u]) }