Initial QSfera import
This commit is contained in:
+303
@@ -0,0 +1,303 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user