diff options
Diffstat (limited to 'guestmetric')
-rw-r--r-- | guestmetric/guestmetric.go | 38 | ||||
-rw-r--r-- | guestmetric/guestmetric_linux.go | 257 | ||||
-rw-r--r-- | guestmetric/guestmetric_test.go | 63 |
3 files changed, 358 insertions, 0 deletions
diff --git a/guestmetric/guestmetric.go b/guestmetric/guestmetric.go new file mode 100644 index 0000000..836f105 --- /dev/null +++ b/guestmetric/guestmetric.go @@ -0,0 +1,38 @@ +package guestmetric + +import ( + "bytes" + "os/exec" +) + +type GuestMetric map[string]string + +type CollectFunc func() (GuestMetric, error) + +type GuestMetricsCollector interface { + CollectOS() (GuestMetric, error) + CollectMisc() (GuestMetric, error) + CollectNetworkAddr() (GuestMetric, error) + CollectDisk() (GuestMetric, error) + CollectMemory() (GuestMetric, error) +} + +func runCmd(name string, args ...string) (output string, err error) { + cmd := exec.Command(name, args...) + var out bytes.Buffer + cmd.Stdout = &out + err = cmd.Run() + if err != nil { + return "", err + } + output = out.String() + return output, nil +} + +func prefixKeys(prefix string, m GuestMetric) GuestMetric { + m1 := make(GuestMetric, 0) + for k, v := range m { + m1[prefix+k] = v + } + return m1 +} diff --git a/guestmetric/guestmetric_linux.go b/guestmetric/guestmetric_linux.go new file mode 100644 index 0000000..3970da4 --- /dev/null +++ b/guestmetric/guestmetric_linux.go @@ -0,0 +1,257 @@ +package guestmetric + +import ( + xenstoreclient "../xenstoreclient" + "bufio" + "bytes" + "fmt" + "os" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" +) + +type Collector struct { + Client xenstoreclient.XenStoreClient + Ballon bool + Debug bool +} + +func (c *Collector) CollectOS() (GuestMetric, error) { + current := make(GuestMetric, 0) + f, err := os.OpenFile("/var/cache/xe-linux-distribution", os.O_RDONLY, 0666) + if err != nil { + return nil, err + } + defer f.Close() + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := scanner.Text() + if strings.Contains(line, "=") { + parts := strings.SplitN(line, "=", 2) + k := strings.TrimSpace(parts[0]) + v := strings.TrimSpace(strings.Trim(strings.TrimSpace(parts[1]), "\"")) + current[k] = v + } + } + return prefixKeys("data/", current), nil +} + +func (c *Collector) CollectMisc() (GuestMetric, error) { + current := make(GuestMetric, 0) + if c.Ballon { + current["control/feature-balloon"] = "1" + } else { + current["control/feature-balloon"] = "0" + } + current["attr/PVAddons/Installed"] = "1" + current["attr/PVAddons/MajorVersion"] = "@PRODUCT_MAJOR_VERSION@" + current["attr/PVAddons/MinorVersion"] = "@PRODUCT_MINOR_VERSION@" + current["attr/PVAddons/MicroVersion"] = "@PRODUCT_MICRO_VERSION@" + current["attr/PVAddons/BuildVersion"] = "@NUMERIC_BUILD_NUMBER@" + + return current, nil +} + +func (c *Collector) CollectMemory() (GuestMetric, error) { + current := make(GuestMetric, 0) + f, err := os.OpenFile("/proc/meminfo", os.O_RDONLY, 0666) + if err != nil { + return nil, err + } + defer f.Close() + scanner := bufio.NewScanner(f) + for scanner.Scan() { + parts := regexp.MustCompile(`\w+`).FindAllString(scanner.Text(), -1) + switch parts[0] { + case "MemTotal": + current["meminfo_total"] = parts[1] + case "MemFree": + current["meminfo_free"] = parts[1] + } + } + return prefixKeys("data/", current), nil +} + +func EnumNetworkAddresses(iface string) (GuestMetric, error) { + const ( + IP_RE string = `(\d{1,3}\.){3}\d{1,3}` + IPV6_RE string = `[\da-f:]+[\da-f]` + ) + + var ( + IP_IPV4_ADDR_RE = regexp.MustCompile(`inet\s*(` + IP_RE + `).*\se[a-zA-Z0-9]+\s`) + IP_IPV6_ADDR_RE = regexp.MustCompile(`inet6\s*(` + IPV6_RE + `)`) + IFCONFIG_IPV4_ADDR_RE = regexp.MustCompile(`inet addr:\s*(` + IP_RE + `)`) + IFCONFIG_IPV6_ADDR_RE = regexp.MustCompile(`inet6 addr:\s*(` + IPV6_RE + `)`) + ) + + d := make(GuestMetric, 0) + + var v4re, v6re *regexp.Regexp + var out string + var err error + if out, err = runCmd("ip", "addr", "show", iface); err == nil { + v4re = IP_IPV4_ADDR_RE + v6re = IP_IPV6_ADDR_RE + } else if out, err = runCmd("ifconfig", iface); err == nil { + v4re = IFCONFIG_IPV4_ADDR_RE + v6re = IFCONFIG_IPV6_ADDR_RE + } else { + return nil, fmt.Errorf("Cannot found ip/ifconfig command") + } + + m := v4re.FindAllStringSubmatch(out, -1) + if m != nil { + for _, parts := range m { + d["ip"] = parts[1] + } + } + m = v6re.FindAllStringSubmatch(out, -1) + if m != nil { + for i, parts := range m { + d[fmt.Sprintf("ipv6/%d/addr", i)] = parts[1] + } + } + return d, nil +} + +func (c *Collector) CollectNetworkAddr() (GuestMetric, error) { + current := make(GuestMetric, 0) + + paths, err := filepath.Glob("/sys/class/net/e*") + if err != nil { + return nil, err + } + + for _, path := range paths { + iface := filepath.Base(path) + if addrs, err := EnumNetworkAddresses(iface); err == nil { + for tag, addr := range addrs { + current[fmt.Sprintf("%s/%s", iface, tag)] = addr + } + } + } + return prefixKeys("attr/", current), nil +} + +func readSysfs(filename string) (string, error) { + f, err := os.OpenFile(filename, os.O_RDONLY, 0666) + if err != nil { + return "", err + } + defer f.Close() + scanner := bufio.NewScanner(f) + scanner.Scan() + return scanner.Text(), nil +} + +func (c *Collector) CollectDisk() (GuestMetric, error) { + pi := make(GuestMetric, 0) + + disks := make([]string, 0) + paths, err := filepath.Glob("/sys/block/*/device") + if err != nil { + return nil, err + } + for _, path := range paths { + disk := filepath.Base(strings.TrimSuffix(filepath.Dir(path), "/")) + disks = append(disks, disk) + } + + var sortedDisks sort.StringSlice = disks + sortedDisks.Sort() + + part_idx := 0 + for _, disk := range sortedDisks[:] { + paths, err = filepath.Glob(fmt.Sprintf("/dev/%s?*", disk)) + if err != nil { + return nil, err + } + for _, path := range paths { + p := filepath.Base(path) + line, err := readSysfs(fmt.Sprintf("/sys/block/%s/%s/size", disk, p)) + if err != nil { + return nil, err + } + size, err := strconv.ParseInt(line, 10, 64) + if err != nil { + return nil, err + } + blocksize := 512 + if bs, err := readSysfs(fmt.Sprintf("/sys/block/%s/queue/physical_block_size", p)); err == nil { + if bs1, err := strconv.Atoi(bs); err == nil { + blocksize = bs1 + } + } + real_dev := "" + if c.Client != nil { + nodename, err := readSysfs(fmt.Sprintf("/sys/block/%s/device/nodename", disk)) + if err != nil { + return nil, err + } + backend, err := c.Client.Read(fmt.Sprintf("%s/backend", nodename)) + if err != nil { + return nil, err + } + real_dev, err = c.Client.Read(fmt.Sprintf("%s/dev", backend)) + if err != nil { + return nil, err + } + } + name := path + blkid, err := runCmd("blkid", "-s", "UUID", path) + if err != nil { + return nil, err + } + if strings.Contains(blkid, "=") { + parts := strings.SplitN(strings.TrimSpace(blkid), "=", 2) + name = fmt.Sprintf("%s(%s)", name, strings.Trim(parts[1], "\"")) + } + i := map[string]string{ + "extents/0": real_dev, + "name": name, + "size": strconv.FormatInt(size*int64(blocksize), 10), + } + output, err := runCmd("pvs", "--noheadings", "--units", "b", path) + if err == nil && output != "" { + parts := regexp.MustCompile(`\s+`).Split(output, -1)[1:] + i["free"] = strings.TrimSpace(parts[5])[:len(parts[5])-1] + i["filesystem"] = strings.TrimSpace(parts[2]) + i["mount_points/0"] = "[LVM]" + } else { + output, err = runCmd("mount") + if err == nil { + m := regexp.MustCompile(`(?m)^(\S+) on (\S+) type (\S+)`).FindAllStringSubmatch(output, -1) + if m != nil { + for _, parts := range m { + if parts[1] == path { + i["mount_points/0"] = parts[2] + i["filesystem"] = parts[3] + break + } + } + } + } + output, err = runCmd("df", path) + if err == nil { + scanner := bufio.NewScanner(bytes.NewReader([]byte(output))) + scanner.Scan() + scanner.Scan() + parts := regexp.MustCompile(`\s+`).Split(scanner.Text(), -1) + free, err := strconv.ParseInt(parts[3], 10, 64) + if err == nil { + i["free"] = strconv.FormatInt(free*1024, 10) + } + } + } + for k, v := range i { + pi[fmt.Sprintf("data/volumes/%d/%s", part_idx, k)] = v + } + part_idx += 1 + } + } + return pi, nil +} diff --git a/guestmetric/guestmetric_test.go b/guestmetric/guestmetric_test.go new file mode 100644 index 0000000..dcc5384 --- /dev/null +++ b/guestmetric/guestmetric_test.go @@ -0,0 +1,63 @@ +package guestmetric + +import ( + "testing" +) + +func TestCollector(t *testing.T) { + c := Collector{ + Client: nil, + } + + funcs := []CollectFunc{ + c.CollectDisk, + c.CollectMemory, + c.CollectMisc, + c.CollectNetworkAddr, + } + + for _, f := range funcs { + metric, err := f() + if err != nil { + t.Errorf("%#v error: %#v\n", f, err) + } + t.Logf("%#v return %#v", f, metric) + } +} + +func doBenchmark(b *testing.B, f CollectFunc) { + b.Logf("doBenchmark 1000 for %#v", f) + for i := 0; i < 1000; i++ { + if _, err := f(); err != nil { + b.Errorf("%#v error: %#v\n", f, err) + } + } +} + +func BenchmarkCollectorDisk(b *testing.B) { + c := Collector{ + Client: nil, + } + doBenchmark(b, c.CollectDisk) +} + +func BenchmarkCollectMemory(b *testing.B) { + c := Collector{ + Client: nil, + } + doBenchmark(b, c.CollectMemory) +} + +func BenchmarkCollectMisc(b *testing.B) { + c := Collector{ + Client: nil, + } + doBenchmark(b, c.CollectMisc) +} + +func BenchmarkCollectNetwork(b *testing.B) { + c := Collector{ + Client: nil, + } + doBenchmark(b, c.CollectNetworkAddr) +} |