|
|
|
/*
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"net"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"io"
|
|
|
|
"time"
|
|
|
|
"sort"
|
|
|
|
"bufio"
|
|
|
|
"strings"
|
|
|
|
"strconv"
|
|
|
|
"encoding/xml"
|
|
|
|
)
|
|
|
|
|
|
|
|
type GangliaXML struct {
|
|
|
|
Cluster Cluster `xml:"CLUSTER"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Cluster struct {
|
|
|
|
Name string `xml:"NAME,attr"`
|
|
|
|
Hosts []Host `xml:"HOST"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Host struct {
|
|
|
|
Name string `xml:"NAME,attr"`
|
|
|
|
Ip string `xml:"IP,attr"`
|
|
|
|
Time int64 `xml:"REPORTED,attr"`
|
|
|
|
StartTime int64 `xml:"GMOND_STARTED,attr"`
|
|
|
|
Metrics []Metric `xml:"METRIC"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Metric struct {
|
|
|
|
Name string `xml:"NAME,attr"`
|
|
|
|
Value string `xml:"VAL,attr"`
|
|
|
|
Unit string `xml:"UNITS,attr"`
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func HostRow(host Host) string {
|
|
|
|
cpu := -1.0
|
|
|
|
mem_tot := 0.0
|
|
|
|
mem_free := 0.0
|
|
|
|
load1 := -1.0
|
|
|
|
load5 := -1.0
|
|
|
|
load15 := -1.0
|
|
|
|
|
|
|
|
// Get relevant metrics
|
|
|
|
for _, metric := range host.Metrics {
|
|
|
|
if metric.Name == "cpu_idle" {
|
|
|
|
cpu, _ = strconv.ParseFloat(metric.Value, 32)
|
|
|
|
cpu = 100 - cpu
|
|
|
|
}
|
|
|
|
if metric.Name == "mem_free" {
|
|
|
|
mem_free, _ = strconv.ParseFloat(metric.Value, 32)
|
|
|
|
}
|
|
|
|
if metric.Name == "mem_total" {
|
|
|
|
mem_tot, _ = strconv.ParseFloat(metric.Value, 32)
|
|
|
|
}
|
|
|
|
if metric.Name == "load_one" {
|
|
|
|
load1, _ = strconv.ParseFloat(metric.Value, 32)
|
|
|
|
}
|
|
|
|
if metric.Name == "load_five" {
|
|
|
|
load5, _ = strconv.ParseFloat(metric.Value, 32)
|
|
|
|
}
|
|
|
|
if metric.Name == "load_fifteen" {
|
|
|
|
load15, _ = strconv.ParseFloat(metric.Value, 32)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mem := -1.0
|
|
|
|
if mem_tot > 0 && mem_free > 0 {
|
|
|
|
mem_used := mem_tot - mem_free
|
|
|
|
mem = mem_used/mem_tot
|
|
|
|
}
|
|
|
|
|
|
|
|
then := time.Unix(host.Time, 0) // In UTC
|
|
|
|
time := then.Format("2006-01-02-15:04:05")
|
|
|
|
|
|
|
|
return fmt.Sprintf("%25s\t%15s\t%20s%5.0f %%\t%5.1f %%\t%5.1f %5.1f %5.1f", host.Name, host.Ip, time, cpu, mem*100.0, load1, load5, load15)
|
|
|
|
}
|
|
|
|
|
|
|
|
func readStream(reader io.Reader) ([]byte, error) {
|
|
|
|
data := make([]byte, 0)
|
|
|
|
buf := make([]byte, 2048)
|
|
|
|
|
|
|
|
for {
|
|
|
|
n, err := reader.Read(buf)
|
|
|
|
if err != nil {
|
|
|
|
if err == io.EOF {
|
|
|
|
break
|
|
|
|
} else {
|
|
|
|
return data, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if n == 0 {
|
|
|
|
break
|
|
|
|
} else {
|
|
|
|
data = append(data, buf[:n]...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return data, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func toUTF8(data []byte) []byte {
|
|
|
|
str := string(data)
|
|
|
|
str = strings.Replace(str, "ISO-8859-1", "UTF-8", 1)
|
|
|
|
return []byte(str)
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
args := os.Args
|
|
|
|
port := 8649
|
|
|
|
if len(args) < 2 {
|
|
|
|
fmt.Printf("Usage: %s REMOTE [PORT]\n REMOTE is a ganglia server\n PORT defines the port to query (default: %d)\n", args[0], port)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
remote := args[1] + ":" + strconv.Itoa(port)
|
|
|
|
conn, err := net.Dial("tcp", remote)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintln(os.Stderr, "Connection error: ", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
defer conn.Close()
|
|
|
|
// Read from conn
|
|
|
|
data, err := readStream(bufio.NewReader(conn))
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintln(os.Stderr, "Error reading file: \n", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Hack: Replace "ISO-8859-1" to "UTF-8" in order to make it work with XML
|
|
|
|
data = toUTF8(data)
|
|
|
|
|
|
|
|
// Parse XML
|
|
|
|
var ganglia GangliaXML
|
|
|
|
if err := xml.Unmarshal(data, &ganglia); err != nil {
|
|
|
|
fmt.Fprintln(os.Stderr, "Error parsing xml: ", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Printf("Cluster: %s\n", ganglia.Cluster.Name);
|
|
|
|
hosts := ganglia.Cluster.Hosts
|
|
|
|
sort.Slice(hosts, func(i, j int) bool { return strings.Compare(hosts[i].Name, hosts[j].Name) < 0 })
|
|
|
|
|
|
|
|
// Header
|
|
|
|
fmt.Printf("%25s\t%15s\t%20s%7s\t%7s\t%17s\n", "Host", "Ip", "Last Update", "CPU", "Memory", "Load (1-5-15)")
|
|
|
|
for _, host := range hosts {
|
|
|
|
fmt.Printf("%s\n", HostRow(host))
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|