201 lines
4.5 KiB
Go
201 lines
4.5 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"io/ioutil"
|
||
|
"os"
|
||
|
"runtime"
|
||
|
|
||
|
"github.com/docker/docker/pkg/system"
|
||
|
"github.com/ghodss/yaml"
|
||
|
"github.com/urfave/cli"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
infoDescription = "display system information"
|
||
|
infoCommand = cli.Command{
|
||
|
Name: "info",
|
||
|
Usage: infoDescription,
|
||
|
Description: `Information display here pertain to the host, current storage stats, and build of kpod. Useful for the user and when reporting issues.`,
|
||
|
Flags: infoFlags,
|
||
|
Action: infoCmd,
|
||
|
ArgsUsage: "",
|
||
|
}
|
||
|
infoFlags = []cli.Flag{
|
||
|
cli.BoolFlag{
|
||
|
Name: "debug, D",
|
||
|
Usage: "display additional debug information",
|
||
|
},
|
||
|
cli.BoolFlag{
|
||
|
Name: "json",
|
||
|
Usage: "output as JSON instead of the default YAML",
|
||
|
},
|
||
|
}
|
||
|
)
|
||
|
|
||
|
func infoCmd(c *cli.Context) error {
|
||
|
info := map[string]interface{}{}
|
||
|
|
||
|
infoGivers := []infoGiverFunc{
|
||
|
storeInfo,
|
||
|
hostInfo,
|
||
|
}
|
||
|
|
||
|
if c.Bool("debug") {
|
||
|
infoGivers = append(infoGivers, debugInfo)
|
||
|
}
|
||
|
|
||
|
for _, giver := range infoGivers {
|
||
|
thisName, thisInfo, err := giver(c)
|
||
|
if err != nil {
|
||
|
info[thisName] = infoErr(err)
|
||
|
continue
|
||
|
}
|
||
|
info[thisName] = thisInfo
|
||
|
}
|
||
|
|
||
|
var buf []byte
|
||
|
var err error
|
||
|
if c.Bool("json") {
|
||
|
buf, err = json.MarshalIndent(info, "", " ")
|
||
|
} else {
|
||
|
buf, err = yaml.Marshal(info)
|
||
|
}
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
fmt.Println(string(buf))
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func infoErr(err error) map[string]interface{} {
|
||
|
return map[string]interface{}{
|
||
|
"error": err.Error(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type infoGiverFunc func(c *cli.Context) (name string, info map[string]interface{}, err error)
|
||
|
|
||
|
// top-level "debug" info
|
||
|
func debugInfo(c *cli.Context) (string, map[string]interface{}, error) {
|
||
|
info := map[string]interface{}{}
|
||
|
info["compiler"] = runtime.Compiler
|
||
|
info["go version"] = runtime.Version()
|
||
|
return "debug", info, nil
|
||
|
}
|
||
|
|
||
|
// top-level "host" info
|
||
|
func hostInfo(c *cli.Context) (string, map[string]interface{}, error) {
|
||
|
// lets say OS, arch, number of cpus, amount of memory, maybe os distribution/version, hostname, kernel version, uptime
|
||
|
info := map[string]interface{}{}
|
||
|
info["os"] = runtime.GOOS
|
||
|
info["arch"] = runtime.GOARCH
|
||
|
info["cpus"] = runtime.NumCPU()
|
||
|
mi, err := system.ReadMemInfo()
|
||
|
if err != nil {
|
||
|
info["meminfo"] = infoErr(err)
|
||
|
} else {
|
||
|
// TODO this might be a place for github.com/dustin/go-humanize
|
||
|
info["MemTotal"] = mi.MemTotal
|
||
|
info["MemFree"] = mi.MemFree
|
||
|
info["SwapTotal"] = mi.SwapTotal
|
||
|
info["SwapFree"] = mi.SwapFree
|
||
|
}
|
||
|
if kv, err := readKernelVersion(); err != nil {
|
||
|
info["kernel"] = infoErr(err)
|
||
|
} else {
|
||
|
info["kernel"] = kv
|
||
|
}
|
||
|
|
||
|
if up, err := readUptime(); err != nil {
|
||
|
info["uptime"] = infoErr(err)
|
||
|
} else {
|
||
|
info["uptime"] = up
|
||
|
}
|
||
|
if host, err := os.Hostname(); err != nil {
|
||
|
info["hostname"] = infoErr(err)
|
||
|
} else {
|
||
|
info["hostname"] = host
|
||
|
}
|
||
|
return "host", info, nil
|
||
|
}
|
||
|
|
||
|
// top-level "store" info
|
||
|
func storeInfo(c *cli.Context) (string, map[string]interface{}, error) {
|
||
|
store, err := getStore(c)
|
||
|
if err != nil {
|
||
|
return "store", nil, err
|
||
|
}
|
||
|
|
||
|
// lets say storage driver in use, number of images, number of containers
|
||
|
info := map[string]interface{}{}
|
||
|
info["GraphRoot"] = store.GraphRoot()
|
||
|
info["GraphDriverName"] = store.GraphDriverName()
|
||
|
if is, err := store.ImageStore(); err != nil {
|
||
|
info["ImageStore"] = infoErr(err)
|
||
|
} else {
|
||
|
images, err := is.Images()
|
||
|
if err != nil {
|
||
|
info["ImageStore"] = infoErr(err)
|
||
|
} else {
|
||
|
info["ImageStore"] = map[string]interface{}{
|
||
|
"number": len(images),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/* Oh this is in master on containers/storage, rebase later
|
||
|
if is, err := store.ROImageStores(); err != nil {
|
||
|
info["ROImageStore"] = infoErr(err)
|
||
|
} else {
|
||
|
images, err := is.Images()
|
||
|
if err != nil {
|
||
|
info["ROImageStore"] = infoErr(err)
|
||
|
} else {
|
||
|
info["ROImageStore"] = map[string]interface{}{
|
||
|
"number": len(images),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
*/
|
||
|
if cs, err := store.ContainerStore(); err != nil {
|
||
|
info["ContainerStore"] = infoErr(err)
|
||
|
} else {
|
||
|
containers, err := cs.Containers()
|
||
|
if err != nil {
|
||
|
info["ContainerStore"] = infoErr(err)
|
||
|
} else {
|
||
|
info["ContainerStore"] = map[string]interface{}{
|
||
|
"number": len(containers),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return "store", info, nil
|
||
|
}
|
||
|
|
||
|
func readKernelVersion() (string, error) {
|
||
|
buf, err := ioutil.ReadFile("/proc/version")
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
f := bytes.Fields(buf)
|
||
|
if len(f) < 2 {
|
||
|
return string(bytes.TrimSpace(buf)), nil
|
||
|
}
|
||
|
return string(f[2]), nil
|
||
|
}
|
||
|
|
||
|
func readUptime() (string, error) {
|
||
|
buf, err := ioutil.ReadFile("/proc/uptime")
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
f := bytes.Fields(buf)
|
||
|
if len(f) < 1 {
|
||
|
return "", fmt.Errorf("invalid uptime")
|
||
|
}
|
||
|
return string(f[0]), nil
|
||
|
}
|