1. Update filesystem stats APIs.
2. Add BaseUsage to containers which includes only the rootfs usage, if available. Signed-off-by: Vishnu kannan <vishnuk@google.com>
This commit is contained in:
parent
c285b726ef
commit
6dfdd8eca1
@ -31,6 +31,7 @@ const (
|
||||
containersApi = "containers"
|
||||
subcontainersApi = "subcontainers"
|
||||
machineApi = "machine"
|
||||
machineStatsApi = "machinestats"
|
||||
dockerApi = "docker"
|
||||
summaryApi = "summary"
|
||||
statsApi = "stats"
|
||||
@ -62,6 +63,7 @@ func getApiVersions() []ApiVersion {
|
||||
v1_2 := newVersion1_2(v1_1)
|
||||
v1_3 := newVersion1_3(v1_2)
|
||||
v2_0 := newVersion2_0()
|
||||
v2_1 := newVersion2_1()
|
||||
|
||||
return []ApiVersion{v1_0, v1_1, v1_2, v1_3, v2_0}
|
||||
|
||||
@ -449,6 +451,56 @@ func (self *version2_0) HandleRequest(requestType string, request []string, m ma
|
||||
}
|
||||
}
|
||||
|
||||
type version2_1 struct {
|
||||
baseVersion *version2_0
|
||||
}
|
||||
|
||||
func newVersion2_1(v *version2_0) *version2_1 {
|
||||
return &version2_1{
|
||||
baseVersion: v,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *version2_1) Version() string {
|
||||
return "v2.1"
|
||||
}
|
||||
|
||||
func (self *version2_1) SupportedRequestTypes() []string {
|
||||
return self.baseVersion.SupportedRequestTypes()
|
||||
}
|
||||
|
||||
func (self *version2_1) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
|
||||
// Get the query request.
|
||||
opt, err := getContainerInfoRequest(r.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch requestType {
|
||||
case machineStatsApi:
|
||||
glog.V(4).Infof("Api - MachineStats(%v)", request)
|
||||
cont, err := m.GetRequestedContainersInfo("/", opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeResult(convertMachineStats(cont), w)
|
||||
case statsApi:
|
||||
name := getContainerName(request)
|
||||
glog.V(4).Infof("Api - Stats: Looking for stats for container %q, options %+v", name, opt)
|
||||
conts, err := m.GetRequestedContainersInfo(name, opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
contStats := make(map[string][]v2.ContainerStats, 0)
|
||||
for name, cont := range conts {
|
||||
contStats[name] = convertStats(cont)
|
||||
}
|
||||
return writeResult(contStats, w)
|
||||
default:
|
||||
return self.baseVersion.HandleRequest(requestType, request, m, w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func getRequestOptions(r *http.Request) (v2.RequestOptions, error) {
|
||||
supportedTypes := map[string]bool{
|
||||
v2.TypeName: true,
|
||||
|
@ -32,11 +32,13 @@ type fsHandler interface {
|
||||
|
||||
type realFsHandler struct {
|
||||
sync.RWMutex
|
||||
lastUpdate time.Time
|
||||
usageBytes uint64
|
||||
period time.Duration
|
||||
storageDirs []string
|
||||
fsInfo fs.FsInfo
|
||||
lastUpdate time.Time
|
||||
usageBytes uint64
|
||||
baseUsageBytes uint64
|
||||
period time.Duration
|
||||
writable string
|
||||
other string
|
||||
fsInfo fs.FsInfo
|
||||
// Tells the container to stop.
|
||||
stopChan chan struct{}
|
||||
}
|
||||
@ -45,14 +47,16 @@ const longDu = time.Second
|
||||
|
||||
var _ fsHandler = &realFsHandler{}
|
||||
|
||||
func newFsHandler(period time.Duration, storageDirs []string, fsInfo fs.FsInfo) fsHandler {
|
||||
func newFsHandler(period time.Duration, rootfs, other string, fsInfo fs.FsInfo) fsHandler {
|
||||
return &realFsHandler{
|
||||
lastUpdate: time.Time{},
|
||||
usageBytes: 0,
|
||||
period: period,
|
||||
storageDirs: storageDirs,
|
||||
fsInfo: fsInfo,
|
||||
stopChan: make(chan struct{}, 1),
|
||||
lastUpdate: time.Time{},
|
||||
usageBytes: 0,
|
||||
baseUsageBytes: 0,
|
||||
period: period,
|
||||
rootfs: rootfs,
|
||||
other: other,
|
||||
fsInfo: fsInfo,
|
||||
stopChan: make(chan struct{}, 1),
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,19 +65,22 @@ func (fh *realFsHandler) needsUpdate() bool {
|
||||
}
|
||||
|
||||
func (fh *realFsHandler) update() error {
|
||||
var usage uint64
|
||||
for _, dir := range fh.storageDirs {
|
||||
// TODO(Vishh): Add support for external mounts.
|
||||
dirUsage, err := fh.fsInfo.GetDirUsage(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
usage += dirUsage
|
||||
// TODO(vishh): Add support for external mounts.
|
||||
baseUsage, err := fh.fsInfo.GetDirUsage(fh.rootfs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
otherUsage, error := fh.fsInfo.GetDirUsage(fh.other)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fh.Lock()
|
||||
defer fh.Unlock()
|
||||
fh.lastUpdate = time.Now()
|
||||
fh.usageBytes = usage
|
||||
fh.usageBytes = baseUsage + otherUsage
|
||||
fh.baseUsage = baseUsage
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -104,8 +111,8 @@ func (fh *realFsHandler) stop() {
|
||||
close(fh.stopChan)
|
||||
}
|
||||
|
||||
func (fh *realFsHandler) usage() uint64 {
|
||||
func (fh *realFsHandler) usage() (baseUsageBytes, totalUsageBytes uint64) {
|
||||
fh.RLock()
|
||||
defer fh.RUnlock()
|
||||
return fh.usageBytes
|
||||
return fh.baseUsageBytes, fh.usageBytes
|
||||
}
|
||||
|
@ -59,9 +59,9 @@ type dockerContainerHandler struct {
|
||||
// Manager of this container's cgroups.
|
||||
cgroupManager cgroups.Manager
|
||||
|
||||
storageDriver storageDriver
|
||||
fsInfo fs.FsInfo
|
||||
storageDirs []string
|
||||
storageDriver storageDriver
|
||||
fsInfo fs.FsInfo
|
||||
rootfsStorageDir string
|
||||
|
||||
// Time at which this container was created.
|
||||
creationTime time.Time
|
||||
@ -118,14 +118,13 @@ func newDockerContainerHandler(
|
||||
id := ContainerNameToDockerId(name)
|
||||
|
||||
// Add the Containers dir where the log files are stored.
|
||||
storageDirs := []string{path.Join(*dockerRootDir, pathToContainersDir, id)}
|
||||
|
||||
otherStorageDir := path.Join(*dockerRootDir, pathToContainersDir, id)
|
||||
var rootfsStorageDir string
|
||||
switch storageDriver {
|
||||
case aufsStorageDriver:
|
||||
// Add writable layer for aufs.
|
||||
storageDirs = append(storageDirs, path.Join(*dockerRootDir, pathToAufsDir, id))
|
||||
rootfsStorageDir = path.Join(*dockerRootDir, pathToAufsDir, id)
|
||||
case overlayStorageDriver:
|
||||
storageDirs = append(storageDirs, path.Join(*dockerRootDir, pathToOverlayDir, id))
|
||||
rootfsStorageDir = path.Join(*dockerRootDir, pathToOverlayDir, id)
|
||||
}
|
||||
|
||||
handler := &dockerContainerHandler{
|
||||
@ -138,8 +137,8 @@ func newDockerContainerHandler(
|
||||
storageDriver: storageDriver,
|
||||
fsInfo: fsInfo,
|
||||
rootFs: rootFs,
|
||||
storageDirs: storageDirs,
|
||||
fsHandler: newFsHandler(time.Minute, storageDirs, fsInfo),
|
||||
rootfsStorageDir: rootfsStorageDir,
|
||||
fsHandler: newFsHandler(time.Minute, rootfsStorageDir, otherStorageDir, fsInfo),
|
||||
}
|
||||
|
||||
// We assume that if Inspect fails then the container is not known to docker.
|
||||
@ -274,9 +273,7 @@ func (self *dockerContainerHandler) getFsStats(stats *info.ContainerStats) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// As of now we assume that all the storage dirs are on the same device.
|
||||
// The first storage dir will be that of the image layers.
|
||||
deviceInfo, err := self.fsInfo.GetDirFsDevice(self.storageDirs[0])
|
||||
deviceInfo, err := self.fsInfo.GetDirFsDevice(self.rootfsStorageDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -296,7 +293,7 @@ func (self *dockerContainerHandler) getFsStats(stats *info.ContainerStats) error
|
||||
|
||||
fsStat := info.FsStats{Device: deviceInfo.Device, Limit: limit}
|
||||
|
||||
fsStat.Usage = self.fsHandler.usage()
|
||||
fs.Stat.BaseUsage, fsStat.Usage = self.fsHandler.usage()
|
||||
stats.Filesystem = append(stats.Filesystem, fsStat)
|
||||
|
||||
return nil
|
||||
|
@ -398,6 +398,10 @@ type FsStats struct {
|
||||
// Number of bytes that is consumed by the container on this filesystem.
|
||||
Usage uint64 `json:"usage"`
|
||||
|
||||
// Base Usage that is consumed by the container's writable layer.
|
||||
// This field is only applicable for docker container's as of now.
|
||||
BaseUsage uint64 `json:"base_usage"`
|
||||
|
||||
// Number of bytes available for non-root user.
|
||||
Available uint64 `json:"available"`
|
||||
|
||||
|
@ -95,7 +95,7 @@ type ContainerSpec struct {
|
||||
Image string `json:"image,omitempty"`
|
||||
}
|
||||
|
||||
type ContainerStats struct {
|
||||
type DeprecatedContainerStats struct {
|
||||
// The time of this stat point.
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
// CPU statistics
|
||||
@ -124,6 +124,28 @@ type ContainerStats struct {
|
||||
CustomMetrics map[string][]v1.MetricVal `json:"custom_metrics,omitempty"`
|
||||
}
|
||||
|
||||
type ContainerStats struct {
|
||||
// The time of this stat point.
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
// CPU statistics
|
||||
// In nanoseconds (aggregated)
|
||||
Cpu *v1.CpuStats `json:"cpu,omitempty"`
|
||||
// In nanocores per second (instantaneous)
|
||||
CpuInst *CpuInstStats `json:"cpu_inst,omitempty"`
|
||||
// Disk IO statistics
|
||||
DiskIo *v1.DiskIoStats `json:"diskio,omitempty"`
|
||||
// Memory statistics
|
||||
Memory *v1.MemoryStats `json:"memory,omitempty"`
|
||||
// Network statistics
|
||||
Network *NetworkStats `json:"network,omitempty"`
|
||||
// Filesystem statistics
|
||||
Filesystem *FilesystemStats `json:"filesystem,omitempty"`
|
||||
// Task load statistics
|
||||
Load *v1.LoadStats `json:"load_stats,omitempty"`
|
||||
// Custom Metrics
|
||||
CustomMetrics map[string][]v1.MetricVal `json:"custom_metrics,omitempty"`
|
||||
}
|
||||
|
||||
type Percentiles struct {
|
||||
// Indicates whether the stats are present or not.
|
||||
// If true, values below do not have any data.
|
||||
@ -262,3 +284,11 @@ type CpuInstUsage struct {
|
||||
// Unit: nanocores per second
|
||||
System uint64 `json:"system"`
|
||||
}
|
||||
|
||||
// Filesystem usage statistics.
|
||||
type FilesystemStats struct {
|
||||
// Total Number of bytes consumed by container.
|
||||
TotalUsageBytes *uint64 `json:"totalUsageBytes,omitempty"`
|
||||
// Number of bytes consumed by a container through its root filesystem.
|
||||
BaseUsageBytes *uint64 `json:"baseUsageBytes,omitempty"`
|
||||
}
|
||||
|
@ -16,6 +16,8 @@ package v2
|
||||
|
||||
import (
|
||||
// TODO(rjnagal): Move structs from v1.
|
||||
"time"
|
||||
|
||||
"github.com/google/cadvisor/info/v1"
|
||||
)
|
||||
|
||||
@ -86,3 +88,125 @@ func GetAttributes(mi *v1.MachineInfo, vi *v1.VersionInfo) Attributes {
|
||||
InstanceType: mi.InstanceType,
|
||||
}
|
||||
}
|
||||
|
||||
// MachineStats contains usage statistics for the entire machine.
|
||||
type MachineStats struct {
|
||||
// The time of this stat point.
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
// In nanoseconds (aggregated)
|
||||
Cpu *v1.CpuStats `json:"cpu,omitempty"`
|
||||
// In nanocores per second (instantaneous)
|
||||
CpuInst *CpuInstStats `json:"cpu_inst,omitempty"`
|
||||
// Disk IO statistics
|
||||
DiskIo *v1.DiskIoStats `json:"diskio,omitempty"`
|
||||
// Memory statistics
|
||||
Memory *v1.MemoryStats `json:"memory,omitempty"`
|
||||
// Network statistics
|
||||
Network *NetworkStats `json:"network,omitempty"`
|
||||
// Filesystem statistics
|
||||
Filesystem []MachineFsStats `json:"filesystem,omitempty"`
|
||||
// Task load statistics
|
||||
Load *v1.LoadStats `json:"load_stats,omitempty"`
|
||||
}
|
||||
|
||||
// MachineFsStats contains per filesystem capacity and usage information.
|
||||
type MachineFsStats struct {
|
||||
// The block device name associated with the filesystem.
|
||||
Device string `json:"device"`
|
||||
|
||||
// Number of bytes that can be consumed on this filesystem.
|
||||
Capacity *uint64 `json:"capacity,omitempty"`
|
||||
|
||||
// Number of bytes that is currently consumed on this filesystem.
|
||||
Usage *uint64 `json:"usage,omitempty"`
|
||||
|
||||
// Number of bytes available for non-root user on this filesystem.
|
||||
Available *uint64 `json:"available,omitempty"`
|
||||
|
||||
// DiskStats for this device.
|
||||
DiskStats `json:"inline"`
|
||||
}
|
||||
|
||||
// DiskStats contains per partition usage information.
|
||||
// This information is only available at the machine level.
|
||||
type DiskStats struct {
|
||||
// Number of reads completed
|
||||
// This is the total number of reads completed successfully.
|
||||
ReadsCompleted *uint64 `json:"reads_completed,omitempty"`
|
||||
|
||||
// Number of reads merged
|
||||
// Reads and writes which are adjacent to each other may be merged for
|
||||
// efficiency. Thus two 4K reads may become one 8K read before it is
|
||||
// ultimately handed to the disk, and so it will be counted (and queued)
|
||||
// as only one I/O. This field lets you know how often this was done.
|
||||
ReadsMerged *uint64 `json:"reads_merged,omitempty"`
|
||||
|
||||
// Number of sectors read
|
||||
// This is the total number of sectors read successfully.
|
||||
SectorsRead *uint64 `json:"sectors_read,omitempty"`
|
||||
|
||||
// Number of milliseconds spent reading
|
||||
// This is the total number of milliseconds spent by all reads (as
|
||||
// measured from __make_request() to end_that_request_last()).
|
||||
ReadTime *uint64 `json:"read_time,omitempty"`
|
||||
|
||||
// Number of writes completed
|
||||
// This is the total number of writes completed successfully.
|
||||
WritesCompleted *uint64 `json:"writes_completed,omitempty"`
|
||||
|
||||
// Number of writes merged
|
||||
// See the description of reads merged.
|
||||
WritesMerged *uint64 `json:"writes_merged,omitempty"`
|
||||
|
||||
// Number of sectors written
|
||||
// This is the total number of sectors written successfully.
|
||||
SectorsWritten *uint64 `json:"sectors_written,omitempty"`
|
||||
|
||||
// Number of milliseconds spent writing
|
||||
// This is the total number of milliseconds spent by all writes (as
|
||||
// measured from __make_request() to end_that_request_last()).
|
||||
WriteTime *uint64 `json:"write_time,omitempty"`
|
||||
|
||||
// Number of I/Os currently in progress
|
||||
// The only field that should go to zero. Incremented as requests are
|
||||
// given to appropriate struct request_queue and decremented as they finish.
|
||||
IoInProgress *uint64 `json:"io_in_progress,omitempty"`
|
||||
|
||||
// Number of milliseconds spent doing I/Os
|
||||
// This field increases so long as field 9 is nonzero.
|
||||
IoTime *uint64 `json:"io_time,omitempty"`
|
||||
|
||||
// weighted number of milliseconds spent doing I/Os
|
||||
// This field is incremented at each I/O start, I/O completion, I/O
|
||||
// merge, or read of these stats by the number of I/Os in progress
|
||||
// (field 9) times the number of milliseconds spent doing I/O since the
|
||||
// last update of this field. This can provide an easy measure of both
|
||||
// I/O completion time and the backlog that may be accumulating.
|
||||
WeightedIoTime *uint64 `json:"weighted_io_time,omitempty"`
|
||||
}
|
||||
|
||||
func GetMachineFsStats(fsStats []v1.FsStats) []MachineFsStats {
|
||||
var result []MachineFsStats
|
||||
for _, stat := range fsStats {
|
||||
result = append(result, MachineFsStats{
|
||||
Device: stat.Device,
|
||||
Capacity: &stat.Limit,
|
||||
Usage: &stat.Usage,
|
||||
Available: &stat.Available,
|
||||
DiskStats: DiskStats{
|
||||
ReadsCompleted: &stat.ReadsCompleted,
|
||||
ReadsMerged: &stat.ReadsMerged,
|
||||
SectorsRead: &stat.SectorsRead,
|
||||
ReadTime: &stat.ReadTime,
|
||||
WritesCompleted: &stat.WritesCompleted,
|
||||
WritesMerged: &stat.WritesMerged,
|
||||
SectorsWritten: &stat.SectorsWritten,
|
||||
WriteTime: &stat.WriteTime,
|
||||
IoInProgress: &stat.IoInProgress,
|
||||
IoTime: &stat.IoTime,
|
||||
WeightedIoTime: &stat.WeightedIoTime,
|
||||
},
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user