From 408005be94ba0a5e660e6e9567411436d2fc8ad4 Mon Sep 17 00:00:00 2001 From: James Nugent Date: Tue, 14 Mar 2017 12:11:30 -0500 Subject: [PATCH 1/5] host: Add Solaris support for Info, {Boot,Up}time This commit adds support for Info(), BootTime() and Uptime() in package Host. It uses no cgo, preferring to parse the output of `kstat -p` instead. Thanks go to @gfrey for the parsing logic for `/etc/release` and `uname`. --- host/host_fallback.go | 2 +- host/host_solaris.go | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 host/host_solaris.go diff --git a/host/host_fallback.go b/host/host_fallback.go index 0adcdf2..bfd0143 100644 --- a/host/host_fallback.go +++ b/host/host_fallback.go @@ -1,4 +1,4 @@ -// +build !darwin,!linux,!freebsd,!openbsd,!windows +// +build !darwin,!linux,!freebsd,!openbsd,!solaris,!windows package host diff --git a/host/host_solaris.go b/host/host_solaris.go new file mode 100644 index 0000000..79e25d5 --- /dev/null +++ b/host/host_solaris.go @@ -0,0 +1,132 @@ +package host + +import ( + "bufio" + "fmt" + "io/ioutil" + "os" + "os/exec" + "regexp" + "runtime" + "strconv" + "strings" + "time" + + "github.com/shirou/gopsutil/internal/common" +) + +func Info() (*InfoStat, error) { + result := &InfoStat{ + OS: runtime.GOOS, + } + + hostname, err := os.Hostname() + if err != nil { + return nil, err + } + result.Hostname = hostname + + // Parse versions from output of `uname(1)` + uname, err := exec.LookPath("/usr/bin/uname") + if err != nil { + return nil, err + } + + out, err := invoke.Command(uname, "-srv") + if err != nil { + return nil, err + } + + fields := strings.Fields(string(out)) + if len(fields) >= 1 { + result.PlatformFamily = fields[0] + } + if len(fields) >= 2 { + result.KernelVersion = fields[1] + } + if len(fields) == 3 { + result.PlatformVersion = fields[2] + } + + // Find distribution name from /etc/release + fh, err := os.Open("/etc/release") + if err != nil { + return nil, err + } + defer fh.Close() + + sc := bufio.NewScanner(fh) + if sc.Scan() { + line := strings.TrimSpace(sc.Text()) + switch { + case strings.HasPrefix(line, "SmartOS"): + result.Platform = "SmartOS" + case strings.HasPrefix(line, "OpenIndiana"): + result.Platform = "OpenIndiana" + case strings.HasPrefix(line, "OmniOS"): + result.Platform = "OmniOS" + case strings.HasPrefix(line, "Open Storage"): + result.Platform = "NexentaStor" + case strings.HasPrefix(line, "Solaris"): + result.Platform = "Solaris" + case strings.HasPrefix(line, "Oracle Solaris"): + result.Platform = "Solaris" + default: + result.Platform = strings.Fields(line)[0] + } + } + + // Find the boot time and calculate uptime relative to it + bootTime, err := BootTime() + if err != nil { + return nil, err + } + result.BootTime = bootTime + result.Uptime = uptimeSince(bootTime) + + // Count number of processes based on the number of entries in /proc + dirs, err := ioutil.ReadDir("/proc") + if err != nil { + return nil, err + } + result.Procs = uint64(len(dirs)) + + return result, nil +} + +var kstatMatch = regexp.MustCompile(`([^\s]+)[\s]+([^\s]*)`) + +func BootTime() (uint64, error) { + kstat, err := exec.LookPath("/usr/bin/kstat") + if err != nil { + return 0, err + } + + out, err := invoke.Command(kstat, "-p", "unix:0:system_misc:boot_time") + if err != nil { + return 0, err + } + + kstats := kstatMatch.FindAllStringSubmatch(string(out), -1) + if len(kstats) != 1 { + return 0, fmt.Errorf("Expected 1 kstat, found %d", len(kstats)) + } + + return strconv.ParseUint(kstats[0][2], 10, 64) +} + +func Uptime() (uint64, error) { + bootTime, err := BootTime() + if err != nil { + return 0, err + } + return uptimeSince(bootTime), nil +} + +func uptimeSince(since uint64) uint64 { + return uint64(time.Now().Unix()) - since +} + +func Users() ([]UserStat, error) { + return []UserStat{}, common.ErrNotImplementedError +} From ae1d4ba8188c9b51d9a4fc777644edbf2ca62460 Mon Sep 17 00:00:00 2001 From: James Nugent Date: Tue, 14 Mar 2017 12:14:54 -0500 Subject: [PATCH 2/5] Update README.md for Solaris package Host support --- README.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index d381c5f..3291a92 100644 --- a/README.rst +++ b/README.rst @@ -248,12 +248,12 @@ Original Metrics ================== ===== ======= ======= ====== ======= ======= item Linux FreeBSD OpenBSD MacOSX Windows Solaris **HostInfo** -hostname x x x x x - uptime x x x x - proces x x x - os x x x x x - platform x x x x - platformfamily x x x x +hostname x x x x x x + uptime x x x x x + proces x x x x + os x x x x x x + platform x x x x x + platformfamily x x x x x virtualization x **CPU** VendorID x x x x x x From b7760bb5a6e166b42135a9dd61aadbb603011638 Mon Sep 17 00:00:00 2001 From: James Nugent Date: Tue, 14 Mar 2017 14:40:30 -0500 Subject: [PATCH 3/5] memory: Add basic Solaris VirtualMemory() support This commit adds support for VirtualMemory() in package mem. The support only extends to total memory capcity, since that is all that is required in Nomad. It does take into account global versus non-global zones, and does not use cgo. This has been tested inside a zone in the Joyent public cloud for a non-global zone, and in the global zone of a SmartOS virtual machine. --- mem/mem_fallback.go | 2 +- mem/mem_solaris.go | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 mem/mem_solaris.go diff --git a/mem/mem_fallback.go b/mem/mem_fallback.go index b7284b2..e61fe4b 100644 --- a/mem/mem_fallback.go +++ b/mem/mem_fallback.go @@ -1,4 +1,4 @@ -// +build !darwin,!linux,!freebsd,!openbsd,!windows +// +build !darwin,!linux,!freebsd,!openbsd,!solaris,!windows package mem diff --git a/mem/mem_solaris.go b/mem/mem_solaris.go new file mode 100644 index 0000000..9c5111b --- /dev/null +++ b/mem/mem_solaris.go @@ -0,0 +1,109 @@ +package mem + +import ( + "errors" + "fmt" + "os/exec" + "regexp" + "strconv" + "strings" + + "github.com/shirou/gopsutil/internal/common" +) + +// VirtualMemory for Solaris is a minimal implementation which only returns +// what Nomad needs. It does take into account global vs zone, however. +func VirtualMemory() (*VirtualMemoryStat, error) { + result := &VirtualMemoryStat{} + + zoneName, err := zoneName() + if err != nil { + return nil, err + } + + if zoneName == "global" { + cap, err := globalZoneMemoryCapacity() + if err != nil { + return nil, err + } + result.Total = cap + } else { + cap, err := nonGlobalZoneMemoryCapacity() + if err != nil { + return nil, err + } + result.Total = cap + } + + return result, nil +} + +func SwapMemory() (*SwapMemoryStat, error) { + return nil, common.ErrNotImplementedError +} + +func zoneName() (string, error) { + zonename, err := exec.LookPath("/usr/bin/zonename") + if err != nil { + return "", err + } + + out, err := invoke.Command(zonename) + if err != nil { + return "", err + } + + return strings.TrimSpace(string(out)), nil +} + +var globalZoneMemoryCapacityMatch = regexp.MustCompile(`Memory size: ([\d]+) Megabytes`) + +func globalZoneMemoryCapacity() (uint64, error) { + prtconf, err := exec.LookPath("/usr/sbin/prtconf") + if err != nil { + return 0, err + } + + out, err := invoke.Command(prtconf) + if err != nil { + return 0, err + } + + match := globalZoneMemoryCapacityMatch.FindAllStringSubmatch(string(out), -1) + if len(match) != 1 { + return 0, errors.New("Memory size not contained in output of /usr/sbin/prtconf") + } + + totalMB, err := strconv.ParseUint(match[0][1], 10, 64) + if err != nil { + return 0, err + } + + return totalMB * 1024 * 1024, nil +} + +var kstatMatch = regexp.MustCompile(`([^\s]+)[\s]+([^\s]*)`) + +func nonGlobalZoneMemoryCapacity() (uint64, error) { + kstat, err := exec.LookPath("/usr/bin/kstat") + if err != nil { + return 0, err + } + + out, err := invoke.Command(kstat, "-p", "-c", "zone_memory_cap", "memory_cap:*:*:physcap") + if err != nil { + return 0, err + } + + kstats := kstatMatch.FindAllStringSubmatch(string(out), -1) + if len(kstats) != 1 { + return 0, fmt.Errorf("Expected 1 kstat, found %d", len(kstats)) + } + + memSizeBytes, err := strconv.ParseUint(kstats[0][2], 10, 64) + if err != nil { + return 0, err + } + + return memSizeBytes, nil +} From a34d626e17bf27acb1c0a722dd2d09dbc68b32c2 Mon Sep 17 00:00:00 2001 From: James Nugent Date: Tue, 14 Mar 2017 16:22:06 -0500 Subject: [PATCH 4/5] Update README.rst for Solaris VirtualMemory support --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 3291a92..3275970 100644 --- a/README.rst +++ b/README.rst @@ -174,13 +174,13 @@ Current Status - x: work - b: almost works, but something is broken -=================== ====== ======= ======= ====== ======= -name Linux FreeBSD OpenBSD MacOSX Windows +=================== ====== ======= ======= ====== ======= ======= +name Linux FreeBSD OpenBSD MacOSX Windows Solaris cpu_times x x x x x cpu_count x x x x x cpu_percent x x x x x cpu_times_percent x x x x x -virtual_memory x x x x x +virtual_memory x x x x x b swap_memory x x x x disk_partitions x x x x x disk_io_counters x x x From d19ac24a84ebcbc058e00ad3528d78dae9ef2569 Mon Sep 17 00:00:00 2001 From: James Nugent Date: Wed, 15 Mar 2017 08:10:23 -0500 Subject: [PATCH 5/5] mem: Skip TestVirtual_memory on Solaris --- mem/mem_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mem/mem_test.go b/mem/mem_test.go index 346d02f..4a1ae7b 100644 --- a/mem/mem_test.go +++ b/mem/mem_test.go @@ -2,12 +2,17 @@ package mem import ( "fmt" + "runtime" "testing" "github.com/stretchr/testify/assert" ) func TestVirtual_memory(t *testing.T) { + if runtime.GOOS == "solaris" { + t.Skip("Only .Total is supported on Solaris") + } + v, err := VirtualMemory() if err != nil { t.Errorf("error %v", err)