mirror of https://github.com/containers/podman.git
				
				
				
			
		
			
				
	
	
		
			304 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			304 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Go
		
	
	
	
package stats
 | 
						|
 | 
						|
import (
 | 
						|
	"bufio"
 | 
						|
	"bytes"
 | 
						|
	"context"
 | 
						|
	"fmt"
 | 
						|
	"io/ioutil"
 | 
						|
	"net"
 | 
						|
	"os"
 | 
						|
	"path/filepath"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	delim = []byte{' '}
 | 
						|
)
 | 
						|
 | 
						|
// Host represents host status.
 | 
						|
type Host struct {
 | 
						|
	Sysname    string
 | 
						|
	Storages   []*Storage
 | 
						|
	Interfaces []*Interface
 | 
						|
}
 | 
						|
 | 
						|
// MemStats represents the memory statistics.
 | 
						|
type MemStats struct {
 | 
						|
	Total       int64 // total memory in byte
 | 
						|
	PageSize    int64 // a page size in byte
 | 
						|
	KernelPages int64
 | 
						|
	UserPages   Gauge
 | 
						|
	SwapPages   Gauge
 | 
						|
 | 
						|
	Malloced Gauge // kernel malloced data in byte
 | 
						|
	Graphics Gauge // kernel graphics data in byte
 | 
						|
}
 | 
						|
 | 
						|
// Gauge is used/available gauge.
 | 
						|
type Gauge struct {
 | 
						|
	Used  int64
 | 
						|
	Avail int64
 | 
						|
}
 | 
						|
 | 
						|
func (g Gauge) Free() int64 {
 | 
						|
	return g.Avail - g.Used
 | 
						|
}
 | 
						|
 | 
						|
// ReadMemStats reads memory statistics from /dev/swap.
 | 
						|
func ReadMemStats(ctx context.Context, opts ...Option) (*MemStats, error) {
 | 
						|
	cfg := newConfig(opts...)
 | 
						|
	swap := filepath.Join(cfg.rootdir, "/dev/swap")
 | 
						|
	f, err := os.Open(swap)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	defer f.Close()
 | 
						|
 | 
						|
	var stat MemStats
 | 
						|
	m := map[string]interface{}{
 | 
						|
		"memory":        &stat.Total,
 | 
						|
		"pagesize":      &stat.PageSize,
 | 
						|
		"kernel":        &stat.KernelPages,
 | 
						|
		"user":          &stat.UserPages,
 | 
						|
		"swap":          &stat.SwapPages,
 | 
						|
		"kernel malloc": &stat.Malloced,
 | 
						|
		"kernel draw":   &stat.Graphics,
 | 
						|
	}
 | 
						|
	scanner := bufio.NewScanner(f)
 | 
						|
	for scanner.Scan() {
 | 
						|
		fields := bytes.SplitN(scanner.Bytes(), delim, 2)
 | 
						|
		if len(fields) < 2 {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		switch key := string(fields[1]); key {
 | 
						|
		case "memory", "pagesize", "kernel":
 | 
						|
			v := m[key].(*int64)
 | 
						|
			n, err := strconv.ParseInt(string(fields[0]), 10, 64)
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
			*v = n
 | 
						|
		case "user", "swap", "kernel malloc", "kernel draw":
 | 
						|
			v := m[key].(*Gauge)
 | 
						|
			if err := parseGauge(string(fields[0]), v); err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if err := scanner.Err(); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return &stat, nil
 | 
						|
}
 | 
						|
 | 
						|
func parseGauge(s string, r *Gauge) error {
 | 
						|
	a := strings.SplitN(s, "/", 2)
 | 
						|
	if len(a) != 2 {
 | 
						|
		return fmt.Errorf("can't parse ratio: %s", s)
 | 
						|
	}
 | 
						|
	var p intParser
 | 
						|
	u := p.ParseInt64(a[0], 10)
 | 
						|
	n := p.ParseInt64(a[1], 10)
 | 
						|
	if err := p.Err(); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	r.Used = u
 | 
						|
	r.Avail = n
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
type Storage struct {
 | 
						|
	Name     string
 | 
						|
	Model    string
 | 
						|
	Capacity int64
 | 
						|
}
 | 
						|
 | 
						|
type Interface struct {
 | 
						|
	Name string
 | 
						|
	Addr string
 | 
						|
}
 | 
						|
 | 
						|
const (
 | 
						|
	numEther = 8  // see ether(3)
 | 
						|
	numIpifc = 16 // see ip(3)
 | 
						|
)
 | 
						|
 | 
						|
// ReadInterfaces reads network interfaces from etherN.
 | 
						|
func ReadInterfaces(ctx context.Context, opts ...Option) ([]*Interface, error) {
 | 
						|
	cfg := newConfig(opts...)
 | 
						|
	var a []*Interface
 | 
						|
	for i := 0; i < numEther; i++ {
 | 
						|
		p, err := readInterface(cfg.rootdir, i)
 | 
						|
		if os.IsNotExist(err) {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		a = append(a, p)
 | 
						|
	}
 | 
						|
	return a, nil
 | 
						|
}
 | 
						|
 | 
						|
func readInterface(netroot string, i int) (*Interface, error) {
 | 
						|
	ether := fmt.Sprintf("ether%d", i)
 | 
						|
	dir := filepath.Join(netroot, ether)
 | 
						|
	info, err := os.Stat(dir)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if !info.IsDir() {
 | 
						|
		return nil, fmt.Errorf("%s: is not directory", dir)
 | 
						|
	}
 | 
						|
 | 
						|
	addr, err := ioutil.ReadFile(filepath.Join(dir, "addr"))
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return &Interface{
 | 
						|
		Name: ether,
 | 
						|
		Addr: string(addr),
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
var (
 | 
						|
	netdirs = []string{"/net", "/net.alt"}
 | 
						|
)
 | 
						|
 | 
						|
// ReadHost reads host status.
 | 
						|
func ReadHost(ctx context.Context, opts ...Option) (*Host, error) {
 | 
						|
	cfg := newConfig(opts...)
 | 
						|
	var h Host
 | 
						|
	name, err := readSysname(cfg.rootdir)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	h.Sysname = name
 | 
						|
 | 
						|
	a, err := readStorages(cfg.rootdir)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	h.Storages = a
 | 
						|
 | 
						|
	for _, s := range netdirs {
 | 
						|
		netroot := filepath.Join(cfg.rootdir, s)
 | 
						|
		ifaces, err := ReadInterfaces(ctx, WithRootDir(netroot))
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		h.Interfaces = append(h.Interfaces, ifaces...)
 | 
						|
	}
 | 
						|
	return &h, nil
 | 
						|
}
 | 
						|
 | 
						|
func readSysname(rootdir string) (string, error) {
 | 
						|
	file := filepath.Join(rootdir, "/dev/sysname")
 | 
						|
	b, err := ioutil.ReadFile(file)
 | 
						|
	if err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
	return string(bytes.TrimSpace(b)), nil
 | 
						|
}
 | 
						|
 | 
						|
func readStorages(rootdir string) ([]*Storage, error) {
 | 
						|
	sdctl := filepath.Join(rootdir, "/dev/sdctl")
 | 
						|
	f, err := os.Open(sdctl)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	defer f.Close()
 | 
						|
 | 
						|
	var a []*Storage
 | 
						|
	scanner := bufio.NewScanner(f)
 | 
						|
	for scanner.Scan() {
 | 
						|
		fields := bytes.Split(scanner.Bytes(), delim)
 | 
						|
		if len(fields) == 0 {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		exp := string(fields[0]) + "*"
 | 
						|
		if !strings.HasPrefix(exp, "sd") {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		dir := filepath.Join(rootdir, "/dev", exp)
 | 
						|
		m, err := filepath.Glob(dir)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		for _, dir := range m {
 | 
						|
			s, err := readStorage(dir)
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
			a = append(a, s)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if err := scanner.Err(); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return a, nil
 | 
						|
}
 | 
						|
 | 
						|
func readStorage(dir string) (*Storage, error) {
 | 
						|
	ctl := filepath.Join(dir, "ctl")
 | 
						|
	f, err := os.Open(ctl)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	defer f.Close()
 | 
						|
 | 
						|
	var s Storage
 | 
						|
	s.Name = filepath.Base(dir)
 | 
						|
	scanner := bufio.NewScanner(f)
 | 
						|
	for scanner.Scan() {
 | 
						|
		line := scanner.Bytes()
 | 
						|
		switch {
 | 
						|
		case bytes.HasPrefix(line, []byte("inquiry")):
 | 
						|
			s.Model = string(bytes.TrimSpace(line[7:]))
 | 
						|
		case bytes.HasPrefix(line, []byte("geometry")):
 | 
						|
			fields := bytes.Split(line, delim)
 | 
						|
			if len(fields) < 3 {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			var p intParser
 | 
						|
			sec := p.ParseInt64(string(fields[1]), 10)
 | 
						|
			size := p.ParseInt64(string(fields[2]), 10)
 | 
						|
			if err := p.Err(); err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
			s.Capacity = sec * size
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if err := scanner.Err(); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return &s, nil
 | 
						|
}
 | 
						|
 | 
						|
type IPStats struct {
 | 
						|
	ID      int    // number of interface in ipifc dir
 | 
						|
	Device  string // associated physical device
 | 
						|
	MTU     int    // max transfer unit
 | 
						|
	Sendra6 uint8  // on == send router adv
 | 
						|
	Recvra6 uint8  // on == recv router adv
 | 
						|
 | 
						|
	Pktin  int64 // packets read
 | 
						|
	Pktout int64 // packets written
 | 
						|
	Errin  int64 // read errors
 | 
						|
	Errout int64 // write errors
 | 
						|
}
 | 
						|
 | 
						|
type Iplifc struct {
 | 
						|
	IP            net.IP
 | 
						|
	Mask          net.IPMask
 | 
						|
	Net           net.IP // ip & mask
 | 
						|
	PerfLifetime  int64  // preferred lifetime
 | 
						|
	ValidLifetime int64  // valid lifetime
 | 
						|
}
 | 
						|
 | 
						|
type Ipv6rp struct {
 | 
						|
	// TODO(lufia): see ip(2)
 | 
						|
}
 |