// Partitions returns disk partitions. If all is false, returns // physical devices only (e.g. hard disks, cd-rom drives, USB keys) // and ignore all others (e.g. memory partitions such as /dev/shm) // // should use setmntent(3) but this implement use /etc/mtab file func Partitions(all bool) ([]PartitionStat, error) { filename := common.HostEtc("mtab") lines, err := common.ReadLines(filename) if err != nil { return nil, err } fs, err := getFileSystems() if err != nil { return nil, err } ret := make([]PartitionStat, 0, len(lines)) for _, line := range lines { fields := strings.Fields(line) d := PartitionStat{ Device: fields[0], Mountpoint: fields[1], Fstype: fields[2], Opts: fields[3], } if all == false { if d.Device == "none" || !common.StringsHas(fs, d.Fstype) { continue } } ret = append(ret, d) } return ret, nil }
// CgroupCPU returnes specified cgroup id CPU status. // containerID is same as docker id if you use docker. // If you use container via systemd.slice, you could use // containerID = docker-<container id>.scope and base=/sys/fs/cgroup/cpuacct/system.slice/ func CgroupCPU(containerID string, base string) (*cpu.TimesStat, error) { statfile := getCgroupFilePath(containerID, base, "cpuacct", "cpuacct.stat") lines, err := common.ReadLines(statfile) if err != nil { return nil, err } // empty containerID means all cgroup if len(containerID) == 0 { containerID = "all" } ret := &cpu.TimesStat{CPU: containerID} for _, line := range lines { fields := strings.Split(line, " ") if fields[0] == "user" { user, err := strconv.ParseFloat(fields[1], 64) if err == nil { ret.User = float64(user) } } if fields[0] == "system" { system, err := strconv.ParseFloat(fields[1], 64) if err == nil { ret.System = float64(system) } } } return ret, nil }
func IOCounters() (map[string]IOCountersStat, error) { filename := common.HostProc("diskstats") lines, err := common.ReadLines(filename) if err != nil { return nil, err } ret := make(map[string]IOCountersStat, 0) empty := IOCountersStat{} for _, line := range lines { fields := strings.Fields(line) name := fields[2] reads, err := strconv.ParseUint((fields[3]), 10, 64) if err != nil { return ret, err } rbytes, err := strconv.ParseUint((fields[5]), 10, 64) if err != nil { return ret, err } rtime, err := strconv.ParseUint((fields[6]), 10, 64) if err != nil { return ret, err } writes, err := strconv.ParseUint((fields[7]), 10, 64) if err != nil { return ret, err } wbytes, err := strconv.ParseUint((fields[9]), 10, 64) if err != nil { return ret, err } wtime, err := strconv.ParseUint((fields[10]), 10, 64) if err != nil { return ret, err } iotime, err := strconv.ParseUint((fields[12]), 10, 64) if err != nil { return ret, err } d := IOCountersStat{ ReadBytes: rbytes * SectorSize, WriteBytes: wbytes * SectorSize, ReadCount: reads, WriteCount: writes, ReadTime: rtime, WriteTime: wtime, IoTime: iotime, } if d == empty { continue } d.Name = name d.SerialNumber = GetDiskSerialNumber(name) ret[name] = d } return ret, nil }
func processInet(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) { if strings.HasSuffix(file, "6") && !common.PathExists(file) { // IPv6 not supported, return empty. return []connTmp{}, nil } lines, err := common.ReadLines(file) if err != nil { return nil, err } var ret []connTmp // skip first line for _, line := range lines[1:] { l := strings.Fields(line) if len(l) < 10 { continue } laddr := l[1] raddr := l[2] status := l[3] inode := l[9] pid := int32(0) fd := uint32(0) i, exists := inodes[inode] if exists { pid = i[0].pid fd = i[0].fd } if filterPid > 0 && filterPid != pid { continue } if kind.sockType == syscall.SOCK_STREAM { status = TCPStatuses[status] } else { status = "NONE" } la, err := decodeAddress(kind.family, laddr) if err != nil { continue } ra, err := decodeAddress(kind.family, raddr) if err != nil { continue } ret = append(ret, connTmp{ fd: fd, family: kind.family, sockType: kind.sockType, laddr: la, raddr: ra, status: status, pid: pid, }) } return ret, nil }
func finishCPUInfo(c *InfoStat) error { if c.Mhz == 0 { lines, err := common.ReadLines(sysCPUPath(c.CPU, "cpufreq/cpuinfo_max_freq")) if err == nil { value, err := strconv.ParseFloat(lines[0], 64) if err != nil { return err } c.Mhz = value } } if len(c.CoreID) == 0 { lines, err := common.ReadLines(sysCPUPath(c.CPU, "topology/coreId")) if err == nil { c.CoreID = lines[0] } } return nil }
func Info() (*InfoStat, error) { ret := &InfoStat{ OS: runtime.GOOS, } hostname, err := os.Hostname() if err == nil { ret.Hostname = hostname } platform, family, version, err := PlatformInformation() if err == nil { ret.Platform = platform ret.PlatformFamily = family ret.PlatformVersion = version } kernelVersion, err := KernelVersion() if err == nil { ret.KernelVersion = kernelVersion } system, role, err := Virtualization() if err == nil { ret.VirtualizationSystem = system ret.VirtualizationRole = role } boot, err := BootTime() if err == nil { ret.BootTime = boot ret.Uptime = uptime(boot) } if numProcs, err := common.NumProcs(); err == nil { ret.Procs = numProcs } sysProductUUID := common.HostSys("class/dmi/id/product_uuid") switch { case common.PathExists(sysProductUUID): lines, err := common.ReadLines(sysProductUUID) if err == nil && len(lines) > 0 && lines[0] != "" { ret.HostID = lines[0] break } fallthrough default: values, err := common.DoSysctrl("kernel.random.boot_id") if err == nil && len(values) == 1 && values[0] != "" { ret.HostID = values[0] } } return ret, nil }
// NetProtoCounters returns network statistics for the entire system // If protocols is empty then all protocols are returned, otherwise // just the protocols in the list are returned. // Available protocols: // ip,icmp,icmpmsg,tcp,udp,udplite func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { if len(protocols) == 0 { protocols = netProtocols } stats := make([]ProtoCountersStat, 0, len(protocols)) protos := make(map[string]bool, len(protocols)) for _, p := range protocols { protos[p] = true } filename := common.HostProc("net/snmp") lines, err := common.ReadLines(filename) if err != nil { return nil, err } linecount := len(lines) for i := 0; i < linecount; i++ { line := lines[i] r := strings.IndexRune(line, ':') if r == -1 { return nil, errors.New(filename + " is not fomatted correctly, expected ':'.") } proto := strings.ToLower(line[:r]) if !protos[proto] { // skip protocol and data line i++ continue } // Read header line statNames := strings.Split(line[r+2:], " ") // Read data line i++ statValues := strings.Split(lines[i][r+2:], " ") if len(statNames) != len(statValues) { return nil, errors.New(filename + " is not fomatted correctly, expected same number of columns.") } stat := ProtoCountersStat{ Protocol: proto, Stats: make(map[string]int64, len(statNames)), } for j := range statNames { value, err := strconv.ParseInt(statValues[j], 10, 64) if err != nil { return nil, err } stat.Stats[statNames[j]] = value } stats = append(stats, stat) } return stats, nil }
// getCgroupMemFile reads a cgroup file and return the contents as uint64. func getCgroupMemFile(containerID, base, file string) (uint64, error) { statfile := getCgroupFilePath(containerID, base, "memory", file) lines, err := common.ReadLines(statfile) if err != nil { return 0, err } if len(lines) != 1 { return 0, fmt.Errorf("wrong format file: %s", statfile) } return strconv.ParseUint(lines[0], 10, 64) }
func getLSB() (*LSB, error) { ret := &LSB{} if common.PathExists(common.HostEtc("lsb-release")) { contents, err := common.ReadLines(common.HostEtc("lsb-release")) if err != nil { return ret, err // return empty } for _, line := range contents { field := strings.Split(line, "=") if len(field) < 2 { continue } switch field[0] { case "DISTRIB_ID": ret.ID = field[1] case "DISTRIB_RELEASE": ret.Release = field[1] case "DISTRIB_CODENAME": ret.Codename = field[1] case "DISTRIB_DESCRIPTION": ret.Description = field[1] } } } else if common.PathExists("/usr/bin/lsb_release") { lsb_release, err := exec.LookPath("/usr/bin/lsb_release") if err != nil { return ret, err } out, err := exec.Command(lsb_release).Output() if err != nil { return ret, err } for _, line := range strings.Split(string(out), "\n") { field := strings.Split(line, ":") if len(field) < 2 { continue } switch field[0] { case "Distributor ID": ret.ID = field[1] case "Release": ret.Release = field[1] case "Codename": ret.Codename = field[1] case "Description": ret.Description = field[1] } } } return ret, nil }
// Returns only one InfoStat on FreeBSD. The information regarding core // count, however is accurate and it is assumed that all InfoStat attributes // are the same across CPUs. func Info() ([]InfoStat, error) { const dmesgBoot = "/var/run/dmesg.boot" lines, _ := common.ReadLines(dmesgBoot) c := InfoStat{} var vals []string var err error if vals, err = common.DoSysctrl("hw.clockrate"); err != nil { return nil, err } if c.Mhz, err = strconv.ParseFloat(vals[0], 64); err != nil { return nil, fmt.Errorf("Unable to parse FreeBSD CPU clock rate: %v", err) } c.CPU = int32(c.Mhz) if vals, err = common.DoSysctrl("hw.ncpu"); err != nil { return nil, err } var i64 int64 if i64, err = strconv.ParseInt(vals[0], 10, 32); err != nil { return nil, fmt.Errorf("Unable to parse FreeBSD cores: %v", err) } c.Cores = int32(i64) if vals, err = common.DoSysctrl("hw.model"); err != nil { return nil, err } c.ModelName = strings.Join(vals, " ") for _, line := range lines { if matches := regexp.MustCompile(`Origin\s*=\s*"(.+)"\s+Id\s*=\s*(.+)\s+Family\s*=\s*(.+)\s+Model\s*=\s*(.+)\s+Stepping\s*=\s*(.+)`).FindStringSubmatch(line); matches != nil { c.VendorID = matches[1] c.Family = matches[3] c.Model = matches[4] t, err := strconv.ParseInt(matches[5], 10, 32) if err != nil { return nil, fmt.Errorf("Unable to parse FreeBSD CPU stepping information from %q: %v", line, err) } c.Stepping = int32(t) } else if matches := regexp.MustCompile(`Features=.+<(.+)>`).FindStringSubmatch(line); matches != nil { for _, v := range strings.Split(matches[1], ",") { c.Flags = append(c.Flags, strings.ToLower(v)) } } else if matches := regexp.MustCompile(`Features2=[a-f\dx]+<(.+)>`).FindStringSubmatch(line); matches != nil { for _, v := range strings.Split(matches[1], ",") { c.Flags = append(c.Flags, strings.ToLower(v)) } } } return []InfoStat{c}, nil }
func VirtualMemory() (*VirtualMemoryStat, error) { filename := common.HostProc("meminfo") lines, _ := common.ReadLines(filename) // flag if MemAvailable is in /proc/meminfo (kernel 3.14+) memavail := false ret := &VirtualMemoryStat{} for _, line := range lines { fields := strings.Split(line, ":") if len(fields) != 2 { continue } key := strings.TrimSpace(fields[0]) value := strings.TrimSpace(fields[1]) value = strings.Replace(value, " kB", "", -1) t, err := strconv.ParseUint(value, 10, 64) if err != nil { return ret, err } switch key { case "MemTotal": ret.Total = t * 1024 case "MemFree": ret.Free = t * 1024 case "MemAvailable": memavail = true ret.Available = t * 1024 case "Buffers": ret.Buffers = t * 1024 case "Cached": ret.Cached = t * 1024 case "Active": ret.Active = t * 1024 case "Inactive": ret.Inactive = t * 1024 case "Writeback": ret.Writeback = t * 1024 case "WritebackTmp": ret.WritebackTmp = t * 1024 case "Dirty": ret.Dirty = t * 1024 } } if !memavail { ret.Available = ret.Free + ret.Buffers + ret.Cached } ret.Used = ret.Total - ret.Available ret.UsedPercent = float64(ret.Total-ret.Available) / float64(ret.Total) * 100.0 return ret, nil }
func processUnix(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) { lines, err := common.ReadLines(file) if err != nil { return nil, err } var ret []connTmp // skip first line for _, line := range lines[1:] { tokens := strings.Fields(line) if len(tokens) < 6 { continue } st, err := strconv.Atoi(tokens[4]) if err != nil { return nil, err } inode := tokens[6] var pairs []inodeMap pairs, exists := inodes[inode] if !exists { pairs = []inodeMap{ inodeMap{}, } } for _, pair := range pairs { if filterPid > 0 && filterPid != pair.pid { continue } var path string if len(tokens) == 8 { path = tokens[len(tokens)-1] } ret = append(ret, connTmp{ fd: pair.fd, family: kind.family, sockType: uint32(st), laddr: Addr{ IP: path, }, pid: pair.pid, status: "NONE", path: path, }) } } return ret, nil }
func KernelVersion() (version string, err error) { filename := common.HostProc("sys/kernel/osrelease") if common.PathExists(filename) { contents, err := common.ReadLines(filename) if err != nil { return "", err } if len(contents) > 0 { version = contents[0] } } return version, nil }
func finishCPUInfo(c *InfoStat) error { var lines []string var err error var value float64 if len(c.CoreID) == 0 { lines, err = common.ReadLines(sysCPUPath(c.CPU, "topology/core_id")) if err == nil { c.CoreID = lines[0] } } // override the value of c.Mhz with cpufreq/cpuinfo_max_freq regardless // of the value from /proc/cpuinfo because we want to report the maximum // clock-speed of the CPU for c.Mhz, matching the behaviour of Windows lines, err = common.ReadLines(sysCPUPath(c.CPU, "cpufreq/cpuinfo_max_freq")) // if we encounter errors below but has a value from parsing /proc/cpuinfo // then we ignore the error if err != nil { if c.Mhz == 0 { return err } else { return nil } } value, err = strconv.ParseFloat(lines[0], 64) if err != nil { if c.Mhz == 0 { return err } else { return nil } } c.Mhz = value / 1000.0 // value is in kHz return nil }
// Returns only one CPUInfoStat on FreeBSD func Info() ([]InfoStat, error) { filename := "/var/run/dmesg.boot" lines, _ := common.ReadLines(filename) var ret []InfoStat c := InfoStat{} for _, line := range lines { if matches := regexp.MustCompile(`CPU:\s+(.+) \(([\d.]+).+\)`).FindStringSubmatch(line); matches != nil { c.ModelName = matches[1] t, err := strconv.ParseFloat(matches[2], 64) if err != nil { return ret, nil } c.Mhz = t } else if matches := regexp.MustCompile(`Origin = "(.+)" Id = (.+) Family = (.+) Model = (.+) Stepping = (.+)`).FindStringSubmatch(line); matches != nil { c.VendorID = matches[1] c.Family = matches[3] c.Model = matches[4] t, err := strconv.ParseInt(matches[5], 10, 32) if err != nil { return ret, nil } c.Stepping = int32(t) } else if matches := regexp.MustCompile(`Features=.+<(.+)>`).FindStringSubmatch(line); matches != nil { for _, v := range strings.Split(matches[1], ",") { c.Flags = append(c.Flags, strings.ToLower(v)) } } else if matches := regexp.MustCompile(`Features2=[a-f\dx]+<(.+)>`).FindStringSubmatch(line); matches != nil { for _, v := range strings.Split(matches[1], ",") { c.Flags = append(c.Flags, strings.ToLower(v)) } } else if matches := regexp.MustCompile(`Logical CPUs per core: (\d+)`).FindStringSubmatch(line); matches != nil { // FIXME: no this line? t, err := strconv.ParseInt(matches[1], 10, 32) if err != nil { return ret, nil } c.Cores = int32(t) } } return append(ret, c), nil }
func getOSRelease() (platform string, version string, err error) { contents, err := common.ReadLines(common.HostEtc("os-release")) if err != nil { return "", "", nil // return empty } for _, line := range contents { field := strings.Split(line, "=") if len(field) < 2 { continue } switch field[0] { case "ID": // use ID for lowercase platform = field[1] case "VERSION": version = field[1] } } return platform, version, nil }
func SwapMemory() (*SwapMemoryStat, error) { sysinfo := &syscall.Sysinfo_t{} if err := syscall.Sysinfo(sysinfo); err != nil { return nil, err } ret := &SwapMemoryStat{ Total: uint64(sysinfo.Totalswap) * uint64(sysinfo.Unit), Free: uint64(sysinfo.Freeswap) * uint64(sysinfo.Unit), } ret.Used = ret.Total - ret.Free //check Infinity if ret.Total != 0 { ret.UsedPercent = float64(ret.Total-ret.Free) / float64(ret.Total) * 100.0 } else { ret.UsedPercent = 0 } filename := common.HostProc("vmstat") lines, _ := common.ReadLines(filename) for _, l := range lines { fields := strings.Fields(l) if len(fields) < 2 { continue } switch fields[0] { case "pswpin": value, err := strconv.ParseUint(fields[1], 10, 64) if err != nil { continue } ret.Sin = value * 4 * 1024 case "pswpout": value, err := strconv.ParseUint(fields[1], 10, 64) if err != nil { continue } ret.Sout = value * 4 * 1024 } } return ret, nil }
// getFileSystems returns supported filesystems from /proc/filesystems func getFileSystems() ([]string, error) { filename := common.HostProc("filesystems") lines, err := common.ReadLines(filename) if err != nil { return nil, err } var ret []string for _, line := range lines { if !strings.HasPrefix(line, "nodev") { ret = append(ret, strings.TrimSpace(line)) continue } t := strings.Split(line, "\t") if len(t) != 2 || t[1] != "zfs" { continue } ret = append(ret, strings.TrimSpace(t[1])) } return ret, nil }
// BootTime returns the system boot time expressed in seconds since the epoch. func BootTime() (uint64, error) { filename := common.HostProc("stat") lines, err := common.ReadLines(filename) if err != nil { return 0, err } for _, line := range lines { if strings.HasPrefix(line, "btime") { f := strings.Fields(line) if len(f) != 2 { return 0, fmt.Errorf("wrong btime format") } b, err := strconv.ParseInt(f[1], 10, 64) if err != nil { return 0, err } return uint64(b), nil } } return 0, fmt.Errorf("could not find btime") }
// Get disk partitions. // should use setmntent(3) but this implement use /etc/mtab file func Partitions(all bool) ([]PartitionStat, error) { filename := common.HostEtc("mtab") lines, err := common.ReadLines(filename) if err != nil { return nil, err } ret := make([]PartitionStat, 0, len(lines)) for _, line := range lines { fields := strings.Fields(line) d := PartitionStat{ Device: fields[0], Mountpoint: fields[1], Fstype: fields[2], Opts: fields[3], } ret = append(ret, d) } return ret, nil }
// CgroupCPU returnes specified cgroup id CPU status. // containerid is same as docker id if you use docker. // If you use container via systemd.slice, you could use // containerid = docker-<container id>.scope and base=/sys/fs/cgroup/cpuacct/system.slice/ func CgroupCPU(containerid string, base string) (*cpu.CPUTimesStat, error) { if len(base) == 0 { base = common.HostSys("fs/cgroup/cpuacct/docker") } statfile := path.Join(base, containerid, "cpuacct.stat") if _, err := os.Stat(statfile); os.IsNotExist(err) { statfile = path.Join(common.HostSys("fs/cgroup/cpuacct/system.slice"), "docker-"+containerid+".scope", "cpuacct.stat") } lines, err := common.ReadLines(statfile) if err != nil { return nil, err } // empty containerid means all cgroup if len(containerid) == 0 { containerid = "all" } ret := &cpu.CPUTimesStat{CPU: containerid} for _, line := range lines { fields := strings.Split(line, " ") if fields[0] == "user" { user, err := strconv.ParseFloat(fields[1], 64) if err == nil { ret.User = float64(user) } } if fields[0] == "system" { system, err := strconv.ParseFloat(fields[1], 64) if err == nil { ret.System = float64(system) } } } return ret, nil }
func Virtualization() (string, string, error) { var system string var role string filename := common.HostProc("xen") if common.PathExists(filename) { system = "xen" role = "guest" // assume guest if common.PathExists(filename + "/capabilities") { contents, err := common.ReadLines(filename + "/capabilities") if err == nil { if common.StringsHas(contents, "control_d") { role = "host" } } } } filename = common.HostProc("modules") if common.PathExists(filename) { contents, err := common.ReadLines(filename) if err == nil { if common.StringsContains(contents, "kvm") { system = "kvm" role = "host" } else if common.StringsContains(contents, "vboxdrv") { system = "vbox" role = "host" } else if common.StringsContains(contents, "vboxguest") { system = "vbox" role = "guest" } } } filename = common.HostProc("cpuinfo") if common.PathExists(filename) { contents, err := common.ReadLines(filename) if err == nil { if common.StringsHas(contents, "QEMU Virtual CPU") || common.StringsHas(contents, "Common KVM processor") || common.StringsHas(contents, "Common 32-bit KVM processor") { system = "kvm" role = "guest" } } } filename = common.HostProc() if common.PathExists(filename + "/bc/0") { system = "openvz" role = "host" } else if common.PathExists(filename + "/vz") { system = "openvz" role = "guest" } // not use dmidecode because it requires root if common.PathExists(filename + "/self/status") { contents, err := common.ReadLines(filename + "/self/status") if err == nil { if common.StringsHas(contents, "s_context:") || common.StringsHas(contents, "VxID:") { system = "linux-vserver" } // TODO: guest or host } } if common.PathExists(filename + "/self/cgroup") { contents, err := common.ReadLines(filename + "/self/cgroup") if err == nil { if common.StringsHas(contents, "lxc") || common.StringsHas(contents, "docker") { system = "lxc" role = "guest" } else if common.PathExists("/usr/bin/lxc-version") { // TODO: which system = "lxc" role = "host" } } } return system, role, nil }
func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { lines, err := common.ReadLines(filename) if err != nil { return nil, err } statlen := len(lines) - 1 ret := make([]IOCountersStat, 0, statlen) for _, line := range lines[2:] { parts := strings.SplitN(line, ":", 2) if len(parts) != 2 { continue } interfaceName := strings.TrimSpace(parts[0]) if interfaceName == "" { continue } fields := strings.Fields(strings.TrimSpace(parts[1])) bytesRecv, err := strconv.ParseUint(fields[0], 10, 64) if err != nil { return ret, err } packetsRecv, err := strconv.ParseUint(fields[1], 10, 64) if err != nil { return ret, err } errIn, err := strconv.ParseUint(fields[2], 10, 64) if err != nil { return ret, err } dropIn, err := strconv.ParseUint(fields[3], 10, 64) if err != nil { return ret, err } bytesSent, err := strconv.ParseUint(fields[8], 10, 64) if err != nil { return ret, err } packetsSent, err := strconv.ParseUint(fields[9], 10, 64) if err != nil { return ret, err } errOut, err := strconv.ParseUint(fields[10], 10, 64) if err != nil { return ret, err } dropOut, err := strconv.ParseUint(fields[13], 10, 64) if err != nil { return ret, err } nic := IOCountersStat{ Name: interfaceName, BytesRecv: bytesRecv, PacketsRecv: packetsRecv, Errin: errIn, Dropin: dropIn, BytesSent: bytesSent, PacketsSent: packetsSent, Errout: errOut, Dropout: dropOut, } ret = append(ret, nic) } if pernic == false { return getIOCountersAll(ret) } return ret, nil }
// CPUInfo on linux will return 1 item per physical thread. // // CPUs have three levels of counting: sockets, cores, threads. // Cores with HyperThreading count as having 2 threads per core. // Sockets often come with many physical CPU cores. // For example a single socket board with two cores each with HT will // return 4 CPUInfoStat structs on Linux and the "Cores" field set to 1. func Info() ([]InfoStat, error) { filename := common.HostProc("cpuinfo") lines, _ := common.ReadLines(filename) var ret []InfoStat c := InfoStat{CPU: -1, Cores: 1} for _, line := range lines { fields := strings.Split(line, ":") if len(fields) < 2 { continue } key := strings.TrimSpace(fields[0]) value := strings.TrimSpace(fields[1]) switch key { case "processor": if c.CPU >= 0 { err := finishCPUInfo(&c) if err != nil { return ret, err } ret = append(ret, c) } c = InfoStat{Cores: 1} t, err := strconv.ParseInt(value, 10, 64) if err != nil { return ret, err } c.CPU = int32(t) case "vendorId", "vendor_id": c.VendorID = value case "cpu family": c.Family = value case "model": c.Model = value case "model name", "cpu": c.ModelName = value if strings.Contains(value, "POWER8") || strings.Contains(value, "POWER7") { c.Model = strings.Split(value, " ")[0] c.Family = "POWER" c.VendorID = "IBM" } case "stepping", "revision": val := value if key == "revision" { val = strings.Split(value, ".")[0] } t, err := strconv.ParseInt(val, 10, 64) if err != nil { return ret, err } c.Stepping = int32(t) case "cpu MHz", "clock": // treat this as the fallback value, thus we ignore error if t, err := strconv.ParseFloat(strings.Replace(value, "MHz", "", 1), 64); err == nil { c.Mhz = t } case "cache size": t, err := strconv.ParseInt(strings.Replace(value, " KB", "", 1), 10, 64) if err != nil { return ret, err } c.CacheSize = int32(t) case "physical id": c.PhysicalID = value case "core id": c.CoreID = value case "flags", "Features": c.Flags = strings.FieldsFunc(value, func(r rune) bool { return r == ',' || r == ' ' }) } } if c.CPU >= 0 { err := finishCPUInfo(&c) if err != nil { return ret, err } ret = append(ret, c) } return ret, nil }
func CgroupMem(containerid string, base string) (*CgroupMemStat, error) { if len(base) == 0 { base = common.HostSys("fs/cgroup/memory/docker") } statfile := path.Join(base, containerid, "memory.stat") if _, err := os.Stat(statfile); os.IsNotExist(err) { statfile = path.Join(common.HostSys("fs/cgroup/memory/system.slice"), "docker-"+containerid+".scope", "memory.stat") } // empty containerid means all cgroup if len(containerid) == 0 { containerid = "all" } lines, err := common.ReadLines(statfile) if err != nil { return nil, err } ret := &CgroupMemStat{ContainerID: containerid} for _, line := range lines { fields := strings.Split(line, " ") v, err := strconv.ParseUint(fields[1], 10, 64) if err != nil { continue } switch fields[0] { case "cache": ret.Cache = v case "rss": ret.RSS = v case "rss_huge": ret.RSSHuge = v case "mapped_file": ret.MappedFile = v case "pgpgin": ret.Pgpgin = v case "pgpgout": ret.Pgpgout = v case "pgfault": ret.Pgfault = v case "pgmajfault": ret.Pgmajfault = v case "inactive_anon": ret.InactiveAnon = v case "active_anon": ret.ActiveAnon = v case "inactive_file": ret.InactiveFile = v case "active_file": ret.ActiveFile = v case "unevictable": ret.Unevictable = v case "hierarchical_memory_limit": ret.HierarchicalMemoryLimit = v case "total_cache": ret.TotalCache = v case "total_rss": ret.TotalRSS = v case "total_rss_huge": ret.TotalRSSHuge = v case "total_mapped_file": ret.TotalMappedFile = v case "total_pgpgin": ret.TotalPgpgIn = v case "total_pgpgout": ret.TotalPgpgOut = v case "total_pgfault": ret.TotalPgFault = v case "total_pgmajfault": ret.TotalPgMajFault = v case "total_inactive_anon": ret.TotalInactiveAnon = v case "total_active_anon": ret.TotalActiveAnon = v case "total_inactive_file": ret.TotalInactiveFile = v case "total_active_file": ret.TotalActiveFile = v case "total_unevictable": ret.TotalUnevictable = v } } return ret, nil }
func IOCounters() (map[string]IOCountersStat, error) { filename := common.HostProc("diskstats") lines, err := common.ReadLines(filename) if err != nil { return nil, err } ret := make(map[string]IOCountersStat, 0) empty := IOCountersStat{} for _, line := range lines { fields := strings.Fields(line) if len(fields) < 14 { // malformed line in /proc/diskstats, avoid panic by ignoring. continue } name := fields[2] reads, err := strconv.ParseUint((fields[3]), 10, 64) if err != nil { return ret, err } mergedReads, err := strconv.ParseUint((fields[4]), 10, 64) if err != nil { return ret, err } rbytes, err := strconv.ParseUint((fields[5]), 10, 64) if err != nil { return ret, err } rtime, err := strconv.ParseUint((fields[6]), 10, 64) if err != nil { return ret, err } writes, err := strconv.ParseUint((fields[7]), 10, 64) if err != nil { return ret, err } mergedWrites, err := strconv.ParseUint((fields[8]), 10, 64) if err != nil { return ret, err } wbytes, err := strconv.ParseUint((fields[9]), 10, 64) if err != nil { return ret, err } wtime, err := strconv.ParseUint((fields[10]), 10, 64) if err != nil { return ret, err } iopsInProgress, err := strconv.ParseUint((fields[11]), 10, 64) if err != nil { return ret, err } iotime, err := strconv.ParseUint((fields[12]), 10, 64) if err != nil { return ret, err } weightedIO, err := strconv.ParseUint((fields[13]), 10, 64) if err != nil { return ret, err } d := IOCountersStat{ ReadBytes: rbytes * SectorSize, WriteBytes: wbytes * SectorSize, ReadCount: reads, WriteCount: writes, MergedReadCount: mergedReads, MergedWriteCount: mergedWrites, ReadTime: rtime, WriteTime: wtime, IopsInProgress: iopsInProgress, IoTime: iotime, WeightedIO: weightedIO, } if d == empty { continue } d.Name = name d.SerialNumber = GetDiskSerialNumber(name) ret[name] = d } return ret, nil }
func CgroupMem(containerID string, base string) (*CgroupMemStat, error) { statfile := getCgroupFilePath(containerID, base, "memory", "memory.stat") // empty containerID means all cgroup if len(containerID) == 0 { containerID = "all" } lines, err := common.ReadLines(statfile) if err != nil { return nil, err } ret := &CgroupMemStat{ContainerID: containerID} for _, line := range lines { fields := strings.Split(line, " ") v, err := strconv.ParseUint(fields[1], 10, 64) if err != nil { continue } switch fields[0] { case "cache": ret.Cache = v case "rss": ret.RSS = v case "rssHuge": ret.RSSHuge = v case "mappedFile": ret.MappedFile = v case "pgpgin": ret.Pgpgin = v case "pgpgout": ret.Pgpgout = v case "pgfault": ret.Pgfault = v case "pgmajfault": ret.Pgmajfault = v case "inactiveAnon": ret.InactiveAnon = v case "activeAnon": ret.ActiveAnon = v case "inactiveFile": ret.InactiveFile = v case "activeFile": ret.ActiveFile = v case "unevictable": ret.Unevictable = v case "hierarchicalMemoryLimit": ret.HierarchicalMemoryLimit = v case "totalCache": ret.TotalCache = v case "totalRss": ret.TotalRSS = v case "totalRssHuge": ret.TotalRSSHuge = v case "totalMappedFile": ret.TotalMappedFile = v case "totalPgpgin": ret.TotalPgpgIn = v case "totalPgpgout": ret.TotalPgpgOut = v case "totalPgfault": ret.TotalPgFault = v case "totalPgmajfault": ret.TotalPgMajFault = v case "totalInactiveAnon": ret.TotalInactiveAnon = v case "totalActiveAnon": ret.TotalActiveAnon = v case "totalInactiveFile": ret.TotalInactiveFile = v case "totalActiveFile": ret.TotalActiveFile = v case "totalUnevictable": ret.TotalUnevictable = v } } r, err := getCgroupMemFile(containerID, base, "memory.usage_in_bytes") if err == nil { ret.MemUsageInBytes = r } r, err = getCgroupMemFile(containerID, base, "memory.max_usage_in_bytes") if err == nil { ret.MemMaxUsageInBytes = r } r, err = getCgroupMemFile(containerID, base, "memoryLimitInBbytes") if err == nil { ret.MemLimitInBytes = r } r, err = getCgroupMemFile(containerID, base, "memoryFailcnt") if err == nil { ret.MemFailCnt = r } return ret, nil }
// CPUInfo on linux will return 1 item per physical thread. // // CPUs have three levels of counting: sockets, cores, threads. // Cores with HyperThreading count as having 2 threads per core. // Sockets often come with many physical CPU cores. // For example a single socket board with two cores each with HT will // return 4 CPUInfoStat structs on Linux and the "Cores" field set to 1. func Info() ([]InfoStat, error) { filename := common.HostProc("cpuinfo") lines, _ := common.ReadLines(filename) var ret []InfoStat c := InfoStat{CPU: -1, Cores: 1} for _, line := range lines { fields := strings.Split(line, ":") if len(fields) < 2 { continue } key := strings.TrimSpace(fields[0]) value := strings.TrimSpace(fields[1]) switch key { case "processor": if c.CPU >= 0 { err := finishCPUInfo(&c) if err != nil { return ret, err } ret = append(ret, c) } c = InfoStat{Cores: 1} t, err := strconv.ParseInt(value, 10, 64) if err != nil { return ret, err } c.CPU = int32(t) case "vendorId": c.VendorID = value case "cpu family": c.Family = value case "model": c.Model = value case "model name": c.ModelName = value case "stepping": t, err := strconv.ParseInt(value, 10, 64) if err != nil { return ret, err } c.Stepping = int32(t) case "cpu MHz": t, err := strconv.ParseFloat(value, 64) if err != nil { return ret, err } c.Mhz = t case "cache size": t, err := strconv.ParseInt(strings.Replace(value, " KB", "", 1), 10, 64) if err != nil { return ret, err } c.CacheSize = int32(t) case "physical id": c.PhysicalID = value case "core id": c.CoreID = value case "flags", "Features": c.Flags = strings.FieldsFunc(value, func(r rune) bool { return r == ',' || r == ' ' }) } } if c.CPU >= 0 { err := finishCPUInfo(&c) if err != nil { return ret, err } ret = append(ret, c) } return ret, nil }
func PlatformInformation() (platform string, family string, version string, err error) { lsb, err := getLSB() if err != nil { lsb = &LSB{} } if common.PathExists(common.HostEtc("oracle-release")) { platform = "oracle" contents, err := common.ReadLines(common.HostEtc("oracle-release")) if err == nil { version = getRedhatishVersion(contents) } } else if common.PathExists(common.HostEtc("enterprise-release")) { platform = "oracle" contents, err := common.ReadLines(common.HostEtc("enterprise-release")) if err == nil { version = getRedhatishVersion(contents) } } else if common.PathExists(common.HostEtc("debian_version")) { if lsb.ID == "Ubuntu" { platform = "ubuntu" version = lsb.Release } else if lsb.ID == "LinuxMint" { platform = "linuxmint" version = lsb.Release } else { if common.PathExists("/usr/bin/raspi-config") { platform = "raspbian" } else { platform = "debian" } contents, err := common.ReadLines(common.HostEtc("debian_version")) if err == nil { version = contents[0] } } } else if common.PathExists(common.HostEtc("redhat-release")) { contents, err := common.ReadLines(common.HostEtc("redhat-release")) if err == nil { version = getRedhatishVersion(contents) platform = getRedhatishPlatform(contents) } } else if common.PathExists(common.HostEtc("system-release")) { contents, err := common.ReadLines(common.HostEtc("system-release")) if err == nil { version = getRedhatishVersion(contents) platform = getRedhatishPlatform(contents) } } else if common.PathExists(common.HostEtc("gentoo-release")) { platform = "gentoo" contents, err := common.ReadLines(common.HostEtc("gentoo-release")) if err == nil { version = getRedhatishVersion(contents) } } else if common.PathExists(common.HostEtc("SuSE-release")) { contents, err := common.ReadLines(common.HostEtc("SuSE-release")) if err == nil { version = getSuseVersion(contents) platform = getSusePlatform(contents) } // TODO: slackware detecion } else if common.PathExists(common.HostEtc("arch-release")) { platform = "arch" version = lsb.Release } else if lsb.ID == "RedHat" { platform = "redhat" version = lsb.Release } else if lsb.ID == "Amazon" { platform = "amazon" version = lsb.Release } else if lsb.ID == "ScientificSL" { platform = "scientific" version = lsb.Release } else if lsb.ID == "XenServer" { platform = "xenserver" version = lsb.Release } else if lsb.ID != "" { platform = strings.ToLower(lsb.ID) version = lsb.Release } switch platform { case "debian", "ubuntu", "linuxmint", "raspbian": family = "debian" case "fedora": family = "fedora" case "oracle", "centos", "redhat", "scientific", "enterpriseenterprise", "amazon", "xenserver", "cloudlinux", "ibm_powerkvm": family = "rhel" case "suse", "opensuse": family = "suse" case "gentoo": family = "gentoo" case "slackware": family = "slackware" case "arch": family = "arch" case "exherbo": family = "exherbo" } return platform, family, version, nil }