diff --git a/examples/plugins/traffic-control/report.go b/examples/plugins/traffic-control/report.go index ddfce5262..44b9c611d 100644 --- a/examples/plugins/traffic-control/report.go +++ b/examples/plugins/traffic-control/report.go @@ -13,12 +13,23 @@ type report struct { } type topology struct { - Nodes map[string]node `json:"nodes"` - Controls map[string]control `json:"controls"` + Nodes map[string]node `json:"nodes"` + Controls map[string]control `json:"controls"` + MetadataTemplates map[string]metadataTemplate `json:"metadata_templates,omitempty"` +} + +type metadataTemplate struct { + ID string `json:"id"` + Label string `json:"label,omitempty"` // Human-readable descriptor for this row + Truncate int `json:"truncate,omitempty"` // If > 0, truncate the value to this length. + Datatype string `json:"dataType,omitempty"` + Priority float64 `json:"priority,omitempty"` + From string `json:"from,omitempty"` // Defines how to get the value from a report node } type node struct { LatestControls map[string]controlEntry `json:"latestControls,omitempty"` + Latest map[string]stringEntry `json:"latest,omitempty"` } type controlEntry struct { @@ -37,6 +48,11 @@ type control struct { Rank int `json:"rank"` } +type stringEntry struct { + Timestamp time.Time `json:"timestamp"` + Value string `json:"value"` +} + type pluginSpec struct { ID string `json:"id"` Label string `json:"label"` @@ -58,8 +74,9 @@ func NewReporter(store *Store) *Reporter { func (r *Reporter) RawReport() ([]byte, error) { rpt := &report{ Container: topology{ - Nodes: r.getContainerNodes(), - Controls: getTrafficControls(), + Nodes: r.getContainerNodes(), + Controls: getTrafficControls(), + MetadataTemplates: getMetadataTemplate(), }, Plugins: []pluginSpec{ { @@ -120,14 +137,34 @@ func (r *Reporter) getContainerNodes() map[string]node { fallthrough case Running: nodeID := containerIDToNodeID(containerID) + latency, _ := getLatency(container.PID) nodes[nodeID] = node{ LatestControls: getTrafficNodeControls(timestamp, dead), + Latest: map[string]stringEntry{ + "traffic-control-latency": { + Timestamp: timestamp, + Value: latency, + }, + }, } } }) return nodes } +func getMetadataTemplate() map[string]metadataTemplate { + return map[string]metadataTemplate{ + "traffic-control-latency": metadataTemplate{ + ID: "traffic-control-latency", + Label: "Latency", + Truncate: 0, + Datatype: "", + Priority: 13.5, + From: "latest", + }, + } +} + func getTrafficNodeControls(timestamp time.Time, dead bool) map[string]controlEntry { controls := map[string]controlEntry{} entry := controlEntry{ diff --git a/examples/plugins/traffic-control/tc.go b/examples/plugins/traffic-control/tc.go index d9b9819db..5e34aac98 100644 --- a/examples/plugins/traffic-control/tc.go +++ b/examples/plugins/traffic-control/tc.go @@ -46,6 +46,67 @@ func DoTrafficControl(pid int, delay uint32) error { return nil } +func getLatency(pid int) (string, error) { + output := "-" + var err error + if output, err = getStatus(pid); err != nil { + return "-", err + } else if output == "" { + return "-", fmt.Errorf("Error: output is empty") + } + outputSplited := split(output) + for i, s := range outputSplited { + if s == "delay" { + if i < len(outputSplited)-1 { + output = outputSplited[i+1] + } else { + output = "-" + } + return output, nil + } + } + return output, fmt.Errorf("delay not found") +} + +func getPktLoss(pid int) (string, error) { + output := "-" + var err error + if output, err = getStatus(pid); err != nil { + return "-", err + } else if output == "" { + return "-", fmt.Errorf("Error: output is empty") + } + outputSplited := split(output) + for i, s := range outputSplited { + if s == "loss" { + if i < len(outputSplited)-1 { + output = outputSplited[i+1] + } else { + output = "-" + } + return output, nil + } + } + return output, fmt.Errorf("delay not found") +} + +func getStatus(pid int) (string, error) { + cmd := split("tc qdisc show dev eth0") + netNS := fmt.Sprintf("/proc/%d/ns/net", pid) + var output string + err := ns.WithNetNSPath(netNS, func(hostNS ns.NetNS) error { + if cmdOut, err := exec.Command(cmd[0], cmd[1:]...).CombinedOutput(); err != nil { + log.Error(string(cmdOut)) + output = "-" + return fmt.Errorf("failed to execute command: tc qdisc show dev eth0: %v", err) + } else { + output = string(cmdOut) + } + return nil + }) + return output, err +} + func split(cmd string) []string { return strings.Split(cmd, " ") }