From 2e2a31bea28e8f2051f1975b08be64c8f8522524 Mon Sep 17 00:00:00 2001 From: Rohit Jnagal Date: Thu, 12 Mar 2015 06:01:57 +0000 Subject: [PATCH] WIP: Add stats API for 2.0. First cut on 2.0 stats API. Main change in returned stats is that presence checks (eg. HasCPU) are embedded in each stat. Added support to handle querying container with name. Current syntax: /stats?name=/&count=1, /stats?name=docker/928c058ce260ac2a55972b18cb991fa0475fbbb7bc15bd295e62b76964d05fe6 [default count: 64] Other handlers to include: dockerid, dockeralias. We can make subcontainers inclusion as an option too. --- api/versions.go | 74 ++++++++++++++++++++++++++++++++++++++++++++ info/v2/container.go | 36 +++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/api/versions.go b/api/versions.go index fe83a527..c1b9345e 100644 --- a/api/versions.go +++ b/api/versions.go @@ -17,6 +17,7 @@ package api import ( "fmt" "net/http" + "strconv" "github.com/golang/glog" "github.com/google/cadvisor/events" @@ -31,6 +32,7 @@ const ( machineApi = "machine" dockerApi = "docker" summaryApi = "summary" + statsApi = "stats" specApi = "spec" eventsApi = "events" storageApi = "storage" @@ -314,6 +316,22 @@ func (self *version2_0) HandleRequest(requestType string, request []string, m ma } return writeResult(stats, w) + case statsApi: + name := getContainerName(request) + sr, err := getStatsRequest(name, r) + if err != nil { + return err + } + glog.V(2).Infof("Api - Stats: Looking for stats for container %q, options %+v", name, sr) + query := info.ContainerInfoRequest{ + NumStats: sr.Count, + } + cont, err := m.GetContainerInfo(name, &query) + if err != nil { + return fmt.Errorf("failed to get container %q: %v", name, err) + } + contStats := convertStats(cont) + return writeResult(contStats, w) case specApi: containerName := getContainerName(request) glog.V(2).Infof("Api - Spec(%v)", containerName) @@ -365,3 +383,59 @@ func convertSpec(specV1 info.ContainerSpec) v2.ContainerSpec { } return specV2 } + +func convertStats(cont *info.ContainerInfo) []v2.ContainerStats { + stats := []v2.ContainerStats{} + for _, val := range cont.Stats { + stat := v2.ContainerStats{ + Timestamp: val.Timestamp, + HasCpu: cont.Spec.HasCpu, + HasMemory: cont.Spec.HasMemory, + HasNetwork: cont.Spec.HasNetwork, + HasFilesystem: cont.Spec.HasFilesystem, + HasDiskIo: cont.Spec.HasDiskIo, + } + if stat.HasCpu { + stat.Cpu = val.Cpu + } + if stat.HasMemory { + stat.Memory = val.Memory + } + if stat.HasNetwork { + // TODO(rjnagal): Return stats about all network interfaces. + stat.Network = append(stat.Network, val.Network) + } + if stat.HasFilesystem { + stat.Filesystem = val.Filesystem + } + if stat.HasDiskIo { + stat.DiskIo = val.DiskIo + } + // TODO(rjnagal): Handle load stats. + stats = append(stats, stat) + } + return stats +} + +func getStatsRequest(id string, r *http.Request) (v2.StatsRequest, error) { + // fill in the defaults. + sr := v2.StatsRequest{ + IdType: "name", + Count: 64, + Recursive: false, + } + idType := r.URL.Query().Get("type") + if len(idType) != 0 && idType != "name" { + return sr, fmt.Errorf("unknown 'type' %q for container name %q", idType, id) + } + count := r.URL.Query().Get("count") + if len(count) != 0 { + n, err := strconv.ParseUint(count, 10, 32) + if err != nil { + return sr, fmt.Errorf("failed to parse 'count' option: %v", count) + } + sr.Count = int(n) + } + // TODO(rjnagal): Add option to specify recursive. + return sr, nil +} diff --git a/info/v2/container.go b/info/v2/container.go index 33745ec6..c3c6eb68 100644 --- a/info/v2/container.go +++ b/info/v2/container.go @@ -16,6 +16,10 @@ package v2 import ( "time" + + // TODO(rjnagal): Remove dependency after moving all stats structs from v1. + // using v1 now for easy conversion. + "github.com/google/cadvisor/info/v1" ) type CpuSpec struct { @@ -54,6 +58,29 @@ type ContainerSpec struct { Memory MemorySpec `json:"memory,omitempty"` } +type ContainerStats struct { + // The time of this stat point. + Timestamp time.Time `json:"timestamp"` + // CPU statistics + HasCpu bool `json:"has_cpu"` + Cpu v1.CpuStats `json:"cpu,omitempty"` + // Disk IO statistics + HasDiskIo bool `json:"has_diskio"` + DiskIo v1.DiskIoStats `json:"diskio,omitempty"` + // Memory statistics + HasMemory bool `json:"has_memory"` + Memory v1.MemoryStats `json:"memory,omitempty"` + // Network statistics + HasNetwork bool `json:"has_network"` + Network []v1.NetworkStats `json:"network,omitempty"` + // Filesystem statistics + HasFilesystem bool `json:"has_filesystem"` + Filesystem []v1.FsStats `json:"filesystem,omitempty"` + // Task load statistics + HasLoad bool `json:"has_load"` + Load v1.LoadStats `json:"load_stats,omitempty"` +} + type Percentiles struct { // Indicates whether the stats are present or not. // If true, values below do not have any data. @@ -114,3 +141,12 @@ type FsInfo struct { // Labels associated with this filesystem. Labels []string `json:"labels"` } + +type StatsRequest struct { + // Type of container identifier specified - "name", "dockerid", dockeralias" + IdType string `json:"type"` + // Number of stats to return + Count int `json:"count"` + // Whether to include stats for child subcontainers. + Recursive bool `json:"recursive"` +}