mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-03 10:11:03 +00:00
We only want to scope (i.e. prefix with hostID) those addresses that are deemed loopback, to disambiguate them. Otherwise, we want to leave addresses in unscoped form, so they can be matched, and links between communicating nodes properly made. So, we make the isLoopback check in MakeAddressID, and omit hostID if the address isn't loopback. So far so good. But this breaks topology rendering, as we were relying on extracting hostID from adjacency node IDs, to populate origin hosts in the rendered node output. So we need another way to get origin host from an arbitrary node. A survey revealed no reliable way to get that information from IDs in their new form. However, we have access to node metadata, so this changeset introduces the OriginHostTagger, which tags each node with its origin host, via the foreign-key semantics we'll use going forward.
150 lines
5.2 KiB
Go
150 lines
5.2 KiB
Go
package report
|
|
|
|
import (
|
|
"net"
|
|
"strings"
|
|
)
|
|
|
|
// Report is the core data type. It's produced by probes, and consumed and
|
|
// stored by apps. It's composed of multiple topologies, each representing
|
|
// a different (related, but not equivalent) view of the network.
|
|
type Report struct {
|
|
// Endpoint nodes are individual (address, port) tuples on each host.
|
|
// They come from inspecting active connections and can (theoretically)
|
|
// be traced back to a process. Edges are present.
|
|
Endpoint Topology
|
|
|
|
// Address nodes are addresses (e.g. ifconfig) on each host. Certain
|
|
// information may be present in this topology that can't be mapped to
|
|
// endpoints (e.g. ICMP). Edges are present.
|
|
Address Topology
|
|
|
|
// Process nodes are processes on each host. Edges are not present.
|
|
Process Topology
|
|
|
|
// Container nodes represent all Docker containers on hosts running probes.
|
|
// Metadata includes things like Docker image, name etc.
|
|
// Edges are not present.
|
|
Container Topology
|
|
|
|
// Host nodes are physical hosts that run probes. Metadata includes things
|
|
// like operating system, load, etc. The information is scraped by the
|
|
// probes with each published report. Edges are not present.
|
|
Host Topology
|
|
}
|
|
|
|
const (
|
|
// HostNodeID is a metadata foreign key, linking a node in any topology to
|
|
// a node in the host topology. That host node is the origin host, where
|
|
// the node was originally detected.
|
|
HostNodeID = "host_node_id"
|
|
)
|
|
|
|
// RenderableNode is the data type that's yielded to the JavaScript layer as
|
|
// an element of a topology. It should contain information that's relevant
|
|
// to rendering a node when there are many nodes visible at once.
|
|
type RenderableNode struct {
|
|
ID string `json:"id"` //
|
|
LabelMajor string `json:"label_major"` // e.g. "process", human-readable
|
|
LabelMinor string `json:"label_minor,omitempty"` // e.g. "hostname", human-readable, optional
|
|
Rank string `json:"rank"` // to help the layout engine
|
|
Pseudo bool `json:"pseudo,omitempty"` // sort-of a placeholder node, for rendering purposes
|
|
Adjacency IDList `json:"adjacency,omitempty"` // Node IDs (in the same topology domain)
|
|
Origins IDList `json:"origins,omitempty"` // Core node IDs that contributed information
|
|
Metadata AggregateMetadata `json:"metadata"` // Numeric sums
|
|
}
|
|
|
|
// RenderableNodes is a set of RenderableNodes
|
|
type RenderableNodes map[string]RenderableNode
|
|
|
|
// DetailedNode is the data type that's yielded to the JavaScript layer when
|
|
// we want deep information about an individual node.
|
|
type DetailedNode struct {
|
|
ID string `json:"id"`
|
|
LabelMajor string `json:"label_major"`
|
|
LabelMinor string `json:"label_minor,omitempty"`
|
|
Pseudo bool `json:"pseudo,omitempty"`
|
|
Tables []Table `json:"tables"`
|
|
}
|
|
|
|
// Table is a dataset associated with a node. It will be displayed in the
|
|
// detail panel when a user clicks on a node.
|
|
type Table struct {
|
|
Title string `json:"title"` // e.g. Bandwidth
|
|
Numeric bool `json:"numeric"` // should the major column be right-aligned?
|
|
Rows []Row `json:"rows"`
|
|
}
|
|
|
|
// Row is a single entry in a Table dataset.
|
|
type Row struct {
|
|
Key string `json:"key"` // e.g. Ingress
|
|
ValueMajor string `json:"value_major"` // e.g. 25
|
|
ValueMinor string `json:"value_minor,omitempty"` // e.g. KB/s
|
|
}
|
|
|
|
// MakeReport makes a clean report, ready to Merge() other reports into.
|
|
func MakeReport() Report {
|
|
return Report{
|
|
Endpoint: NewTopology(),
|
|
Address: NewTopology(),
|
|
Process: NewTopology(),
|
|
Container: NewTopology(),
|
|
Host: NewTopology(),
|
|
}
|
|
}
|
|
|
|
// Squash squashes all non-local nodes in the report to a super-node called
|
|
// the Internet.
|
|
func (r Report) Squash() Report {
|
|
localNetworks := r.LocalNetworks()
|
|
r.Endpoint = r.Endpoint.Squash(EndpointIDAddresser, localNetworks)
|
|
r.Address = r.Address.Squash(AddressIDAddresser, localNetworks)
|
|
r.Process = r.Process.Squash(PanicIDAddresser, localNetworks)
|
|
r.Container = r.Container.Squash(PanicIDAddresser, localNetworks)
|
|
r.Host = r.Host.Squash(PanicIDAddresser, localNetworks)
|
|
return r
|
|
}
|
|
|
|
// LocalNetworks returns a superset of the networks (think: CIDRs) that are
|
|
// "local" from the perspective of each host represented in the report. It's
|
|
// used to determine which nodes in the report are "remote", i.e. outside of
|
|
// our infrastructure.
|
|
func (r Report) LocalNetworks() []*net.IPNet {
|
|
var ipNets []*net.IPNet
|
|
for _, md := range r.Host.NodeMetadatas {
|
|
val, ok := md["local_networks"]
|
|
if !ok {
|
|
continue
|
|
}
|
|
outer:
|
|
for _, s := range strings.Fields(val) {
|
|
_, ipNet, err := net.ParseCIDR(s)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
for _, existing := range ipNets {
|
|
if ipNet.String() == existing.String() {
|
|
continue outer
|
|
}
|
|
}
|
|
ipNets = append(ipNets, ipNet)
|
|
}
|
|
}
|
|
return ipNets
|
|
}
|
|
|
|
// Topologies returns a slice of Topologies in this report
|
|
func (r Report) Topologies() []Topology {
|
|
return []Topology{r.Endpoint, r.Address, r.Process, r.Container, r.Host}
|
|
}
|
|
|
|
// Validate checks the report for various inconsistencies.
|
|
func (r Report) Validate() error {
|
|
for _, topology := range r.Topologies() {
|
|
if err := topology.Validate(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|