mirror of
https://github.com/weaveworks/scope.git
synced 2026-07-28 01:31:17 +00:00
Merge pull request #274 from tomwilkie/probe-host
Move probe host topology code into its own module
This commit is contained in:
2
Makefile
2
Makefile
@@ -23,7 +23,7 @@ $(SCOPE_EXPORT): $(APP_EXE) $(PROBE_EXE) docker/*
|
||||
|
||||
$(APP_EXE): app/*.go render/*.go report/*.go xfer/*.go
|
||||
|
||||
$(PROBE_EXE): probe/*.go probe/tag/*.go probe/docker/*.go probe/endpoint/*.go probe/process/*.go report/*.go xfer/*.go
|
||||
$(PROBE_EXE): probe/*.go probe/docker/*.go probe/endpoint/*.go probe/host/*.go probe/process/*.go probe/tag/*.go report/*.go xfer/*.go
|
||||
|
||||
$(APP_EXE) $(PROBE_EXE):
|
||||
go get -tags netgo ./$(@D)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/weaveworks/scope/probe/host"
|
||||
"github.com/weaveworks/scope/report"
|
||||
)
|
||||
|
||||
@@ -20,16 +21,16 @@ type OriginHost struct {
|
||||
}
|
||||
|
||||
func getOriginHost(t report.Topology, nodeID string) (OriginHost, bool) {
|
||||
host, ok := t.NodeMetadatas[nodeID]
|
||||
h, ok := t.NodeMetadatas[nodeID]
|
||||
if !ok {
|
||||
return OriginHost{}, false
|
||||
}
|
||||
|
||||
return OriginHost{
|
||||
Hostname: host["host_name"],
|
||||
OS: host["os"],
|
||||
Networks: strings.Split(host["local_networks"], " "),
|
||||
Load: host["load"],
|
||||
Hostname: h[host.HostName],
|
||||
OS: h[host.OS],
|
||||
Networks: strings.Split(h[host.LocalNetworks], " "),
|
||||
Load: h[host.Load],
|
||||
}, true
|
||||
}
|
||||
|
||||
|
||||
128
probe/host/reporter.go
Normal file
128
probe/host/reporter.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package host
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/weaveworks/scope/probe/tag"
|
||||
"github.com/weaveworks/scope/report"
|
||||
)
|
||||
|
||||
// Keys for use in NodeMetadata
|
||||
const (
|
||||
Timestamp = "ts"
|
||||
HostName = "host_name"
|
||||
LocalNetworks = "local_networks"
|
||||
OS = "os"
|
||||
Load = "load"
|
||||
KernelVersion = "kernel_version"
|
||||
Uptime = "uptime"
|
||||
)
|
||||
|
||||
// Exposed for testing
|
||||
const (
|
||||
ProcUptime = "/proc/uptime"
|
||||
ProcLoad = "/proc/loadavg"
|
||||
)
|
||||
|
||||
// Exposed for testing
|
||||
var (
|
||||
InterfaceAddrs = net.InterfaceAddrs
|
||||
Now = func() string { return time.Now().UTC().Format(time.RFC3339Nano) }
|
||||
ReadFile = ioutil.ReadFile
|
||||
Uname = syscall.Uname
|
||||
)
|
||||
|
||||
type reporter struct {
|
||||
hostID string
|
||||
hostName string
|
||||
}
|
||||
|
||||
// NewReporter returns a Reporter which produces a report containing host
|
||||
// topology for this host.
|
||||
func NewReporter(hostID, hostName string) tag.Reporter {
|
||||
return &reporter{
|
||||
hostID: hostID,
|
||||
hostName: hostName,
|
||||
}
|
||||
}
|
||||
|
||||
func getUptime() (time.Duration, error) {
|
||||
var result time.Duration
|
||||
|
||||
buf, err := ReadFile(ProcUptime)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
fields := strings.Fields(string(buf))
|
||||
if len(fields) != 2 {
|
||||
return result, fmt.Errorf("invalid format: %s", string(buf))
|
||||
}
|
||||
|
||||
uptime, err := strconv.ParseFloat(fields[0], 64)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return time.Duration(uptime) * time.Second, nil
|
||||
}
|
||||
|
||||
func charsToString(ca [65]int8) string {
|
||||
s := make([]byte, len(ca))
|
||||
var lens int
|
||||
for ; lens < len(ca); lens++ {
|
||||
if ca[lens] == 0 {
|
||||
break
|
||||
}
|
||||
s[lens] = uint8(ca[lens])
|
||||
}
|
||||
return string(s[0:lens])
|
||||
}
|
||||
|
||||
func (r *reporter) Report() (report.Report, error) {
|
||||
var (
|
||||
rep = report.MakeReport()
|
||||
localCIDRs []string
|
||||
utsname syscall.Utsname
|
||||
)
|
||||
|
||||
localNets, err := InterfaceAddrs()
|
||||
if err != nil {
|
||||
return rep, err
|
||||
}
|
||||
for _, localNet := range localNets {
|
||||
// Not all networks are IP networks.
|
||||
if ipNet, ok := localNet.(*net.IPNet); ok {
|
||||
localCIDRs = append(localCIDRs, ipNet.String())
|
||||
}
|
||||
}
|
||||
|
||||
if err := Uname(&utsname); err != nil {
|
||||
return rep, err
|
||||
}
|
||||
kernel := fmt.Sprintf("%s %s", charsToString(utsname.Release), charsToString(utsname.Version))
|
||||
|
||||
uptime, err := getUptime()
|
||||
if err != nil {
|
||||
return rep, err
|
||||
}
|
||||
|
||||
rep.Host.NodeMetadatas[report.MakeHostNodeID(r.hostID)] = report.NodeMetadata{
|
||||
Timestamp: Now(),
|
||||
HostName: r.hostName,
|
||||
LocalNetworks: strings.Join(localCIDRs, " "),
|
||||
OS: runtime.GOOS,
|
||||
Load: getLoad(),
|
||||
KernelVersion: kernel,
|
||||
Uptime: uptime.String(),
|
||||
}
|
||||
|
||||
return rep, nil
|
||||
}
|
||||
84
probe/host/reporter_test.go
Normal file
84
probe/host/reporter_test.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package host_test
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/weaveworks/scope/probe/host"
|
||||
"github.com/weaveworks/scope/report"
|
||||
"github.com/weaveworks/scope/test"
|
||||
)
|
||||
|
||||
const (
|
||||
procLoad = "0.59 0.36 0.29 1/200 12187"
|
||||
procUptime = "1004143.23 1263220.30"
|
||||
release = "release"
|
||||
version = "version"
|
||||
|
||||
network = "192.168.0.0/16"
|
||||
hostid = "hostid"
|
||||
now = "now"
|
||||
hostname = "hostname"
|
||||
load = "0.59 0.36 0.29"
|
||||
uptime = "278h55m43s"
|
||||
kernel = "release version"
|
||||
)
|
||||
|
||||
func string2c(s string) [65]int8 {
|
||||
var result [65]int8
|
||||
for i, c := range s {
|
||||
result[i] = int8(c)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func TestReporter(t *testing.T) {
|
||||
oldInterfaceAddrs, oldNow, oldReadFile, oldUname := host.InterfaceAddrs, host.Now, host.ReadFile, host.Uname
|
||||
defer func() {
|
||||
host.InterfaceAddrs, host.Now, host.ReadFile, host.Uname = oldInterfaceAddrs, oldNow, oldReadFile, oldUname
|
||||
}()
|
||||
|
||||
host.InterfaceAddrs = func() ([]net.Addr, error) {
|
||||
_, ipnet, _ := net.ParseCIDR(network)
|
||||
return []net.Addr{ipnet}, nil
|
||||
}
|
||||
|
||||
host.Now = func() string { return now }
|
||||
|
||||
host.ReadFile = func(filename string) ([]byte, error) {
|
||||
switch filename {
|
||||
case host.ProcUptime:
|
||||
return []byte(procUptime), nil
|
||||
case host.ProcLoad:
|
||||
return []byte(procLoad), nil
|
||||
default:
|
||||
panic(filename)
|
||||
}
|
||||
}
|
||||
|
||||
host.Uname = func(uts *syscall.Utsname) error {
|
||||
uts.Release = string2c(release)
|
||||
uts.Version = string2c(version)
|
||||
return nil
|
||||
}
|
||||
|
||||
r := host.NewReporter(hostid, hostname)
|
||||
have, _ := r.Report()
|
||||
want := report.MakeReport()
|
||||
want.Host.NodeMetadatas[report.MakeHostNodeID(hostid)] = report.NodeMetadata{
|
||||
host.Timestamp: now,
|
||||
host.HostName: hostname,
|
||||
host.LocalNetworks: network,
|
||||
host.OS: runtime.GOOS,
|
||||
host.Load: load,
|
||||
host.Uptime: uptime,
|
||||
host.KernelVersion: kernel,
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("%s", test.Diff(want, have))
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package host
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,14 +1,13 @@
|
||||
package main
|
||||
package host
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func getLoad() string {
|
||||
buf, err := ioutil.ReadFile("/proc/loadavg")
|
||||
buf, err := ReadFile(ProcLoad)
|
||||
if err != nil {
|
||||
return "unknown"
|
||||
}
|
||||
@@ -3,19 +3,18 @@ package main
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/weaveworks/procspy"
|
||||
"github.com/weaveworks/scope/probe/docker"
|
||||
"github.com/weaveworks/scope/probe/endpoint"
|
||||
"github.com/weaveworks/scope/probe/host"
|
||||
"github.com/weaveworks/scope/probe/process"
|
||||
"github.com/weaveworks/scope/probe/tag"
|
||||
"github.com/weaveworks/scope/report"
|
||||
@@ -80,8 +79,15 @@ func main() {
|
||||
weaveTagger *tag.WeaveTagger
|
||||
)
|
||||
|
||||
taggers := []tag.Tagger{tag.NewTopologyTagger(), tag.NewOriginHostTagger(hostID)}
|
||||
reporters := []tag.Reporter{endpoint.NewReporter(hostID, hostName, *spyProcs)}
|
||||
taggers := []tag.Tagger{
|
||||
tag.NewTopologyTagger(),
|
||||
tag.NewOriginHostTagger(hostID),
|
||||
}
|
||||
|
||||
reporters := []tag.Reporter{
|
||||
host.NewReporter(hostID, hostName),
|
||||
endpoint.NewReporter(hostID, hostName, *spyProcs),
|
||||
}
|
||||
|
||||
if *dockerEnabled && runtime.GOOS == linux {
|
||||
if err = report.AddLocalBridge(*dockerBridge); err != nil {
|
||||
@@ -131,9 +137,6 @@ func main() {
|
||||
r = report.MakeReport()
|
||||
|
||||
case <-spyTick:
|
||||
// Do this every tick so it gets tagged by the OriginHostTagger
|
||||
r.Host = hostTopology(hostID, hostName)
|
||||
|
||||
for _, reporter := range reporters {
|
||||
newReport, err := reporter.Report()
|
||||
if err != nil {
|
||||
@@ -157,29 +160,6 @@ func main() {
|
||||
log.Printf("%s", <-interrupt())
|
||||
}
|
||||
|
||||
// hostTopology produces a host topology for this host. No need to do this
|
||||
// more than once per published report.
|
||||
func hostTopology(hostID, hostName string) report.Topology {
|
||||
var localCIDRs []string
|
||||
if localNets, err := net.InterfaceAddrs(); err == nil {
|
||||
// Not all networks are IP networks.
|
||||
for _, localNet := range localNets {
|
||||
if ipNet, ok := localNet.(*net.IPNet); ok {
|
||||
localCIDRs = append(localCIDRs, ipNet.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
t := report.NewTopology()
|
||||
t.NodeMetadatas[report.MakeHostNodeID(hostID)] = report.NodeMetadata{
|
||||
"ts": time.Now().UTC().Format(time.RFC3339Nano),
|
||||
"host_name": hostName,
|
||||
"local_networks": strings.Join(localCIDRs, " "),
|
||||
"os": runtime.GOOS,
|
||||
"load": getLoad(),
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func interrupt() chan os.Signal {
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/weaveworks/scope/probe/docker"
|
||||
"github.com/weaveworks/scope/probe/host"
|
||||
"github.com/weaveworks/scope/probe/process"
|
||||
"github.com/weaveworks/scope/report"
|
||||
)
|
||||
@@ -208,15 +209,18 @@ func containerImageOriginTable(nmd report.NodeMetadata) (Table, bool) {
|
||||
|
||||
func hostOriginTable(nmd report.NodeMetadata) (Table, bool) {
|
||||
rows := []Row{}
|
||||
if val, ok := nmd["host_name"]; ok {
|
||||
rows = append(rows, Row{"Host name", val, ""})
|
||||
}
|
||||
if val, ok := nmd["load"]; ok {
|
||||
rows = append(rows, Row{"Load", val, ""})
|
||||
}
|
||||
if val, ok := nmd["os"]; ok {
|
||||
rows = append(rows, Row{"Operating system", val, ""})
|
||||
for _, tuple := range []struct{ key, human string }{
|
||||
{host.HostName, "Host name"},
|
||||
{host.Load, "Load"},
|
||||
{host.OS, "Operating system"},
|
||||
{host.KernelVersion, "Kernel version"},
|
||||
{host.Uptime, "Uptime"},
|
||||
} {
|
||||
if val, ok := nmd[tuple.key]; ok {
|
||||
rows = append(rows, Row{Key: tuple.human, ValueMajor: val, ValueMinor: ""})
|
||||
}
|
||||
}
|
||||
|
||||
return Table{
|
||||
Title: "Origin Host",
|
||||
Numeric: false,
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/weaveworks/scope/probe/docker"
|
||||
"github.com/weaveworks/scope/probe/host"
|
||||
"github.com/weaveworks/scope/report"
|
||||
)
|
||||
|
||||
@@ -123,7 +124,7 @@ func MapAddressIdentity(m report.NodeMetadata) (RenderableNode, bool) {
|
||||
func MapHostIdentity(m report.NodeMetadata) (RenderableNode, bool) {
|
||||
var (
|
||||
id = MakeHostID(report.ExtractHostID(m))
|
||||
hostname = m["host_name"]
|
||||
hostname = m[host.HostName]
|
||||
parts = strings.SplitN(hostname, ".", 2)
|
||||
major, minor, rank = "", "", ""
|
||||
)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/weaveworks/scope/probe/host"
|
||||
"github.com/weaveworks/scope/report"
|
||||
)
|
||||
|
||||
@@ -18,7 +19,7 @@ func LocalNetworks(r report.Report) report.Networks {
|
||||
)
|
||||
|
||||
for _, md := range r.Host.NodeMetadatas {
|
||||
val, ok := md["local_networks"]
|
||||
val, ok := md[host.LocalNetworks]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/weaveworks/scope/probe/host"
|
||||
"github.com/weaveworks/scope/render"
|
||||
"github.com/weaveworks/scope/report"
|
||||
"github.com/weaveworks/scope/test"
|
||||
@@ -14,7 +15,7 @@ func TestReportLocalNetworks(t *testing.T) {
|
||||
r := report.MakeReport()
|
||||
r.Merge(report.Report{Host: report.Topology{NodeMetadatas: report.NodeMetadatas{
|
||||
"nonets": {},
|
||||
"foo": {"local_networks": "10.0.0.1/8 192.168.1.1/24 10.0.0.1/8 badnet/33"},
|
||||
"foo": {host.LocalNetworks: "10.0.0.1/8 192.168.1.1/24 10.0.0.1/8 badnet/33"},
|
||||
}}})
|
||||
want := report.Networks([]*net.IPNet{
|
||||
mustParseCIDR("10.0.0.1/8"),
|
||||
|
||||
Reference in New Issue
Block a user