Skip to content

Commit a0a4196

Browse files
committed
Add /storage endpoint to 2.0 API.
/storage returns {device, mountpoint, capacity, usage} for all filesystems. In addition, it also detect and applies label for each filesystem - currently two - "root", "docker-images". /storage/<label> returns info about the filesystem with specific label. eg. /storage/root returns info for root filesystem.
1 parent 62a1788 commit a0a4196

File tree

11 files changed

+221
-25
lines changed

11 files changed

+221
-25
lines changed

api/versions.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const (
3333
summaryApi = "summary"
3434
specApi = "spec"
3535
eventsApi = "events"
36+
storageApi = "storage"
3637
)
3738

3839
// Interface for a cAdvisor API version
@@ -322,6 +323,24 @@ func (self *version2_0) HandleRequest(requestType string, request []string, m ma
322323
}
323324
specV2 := convertSpec(spec)
324325
return writeResult(specV2, w)
326+
case storageApi:
327+
var err error
328+
fi := []v2.FsInfo{}
329+
label := r.URL.Query().Get("label")
330+
if len(label) == 0 {
331+
// Get all global filesystems info.
332+
fi, err = m.GetFsInfo("")
333+
if err != nil {
334+
return err
335+
}
336+
} else {
337+
// Get a specific label.
338+
fi, err = m.GetFsInfo(label)
339+
if err != nil {
340+
return err
341+
}
342+
}
343+
return writeResult(fi, w)
325344
default:
326345
return self.baseVersion.HandleRequest(requestType, request, m, w, r)
327346
}

container/docker/factory.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/golang/glog"
2929
"github.com/google/cadvisor/container"
3030
"github.com/google/cadvisor/container/libcontainer"
31+
"github.com/google/cadvisor/fs"
3132
info "github.com/google/cadvisor/info/v1"
3233
"github.com/google/cadvisor/utils"
3334
)
@@ -62,6 +63,10 @@ func UseSystemd() bool {
6263
return useSystemd
6364
}
6465

66+
func RootDir() string {
67+
return *dockerRootDir
68+
}
69+
6570
type dockerFactory struct {
6671
machineInfoFactory info.MachineInfoFactory
6772

@@ -72,6 +77,9 @@ type dockerFactory struct {
7277

7378
// Information about the mounted cgroup subsystems.
7479
cgroupSubsystems libcontainer.CgroupSubsystems
80+
81+
// Information about mounted filesystems.
82+
fsInfo fs.FsInfo
7583
}
7684

7785
func (self *dockerFactory) String() string {
@@ -87,6 +95,7 @@ func (self *dockerFactory) NewContainerHandler(name string) (handler container.C
8795
client,
8896
name,
8997
self.machineInfoFactory,
98+
self.fsInfo,
9099
*dockerRootDir,
91100
self.usesAufsDriver,
92101
&self.cgroupSubsystems,
@@ -151,7 +160,7 @@ func parseDockerVersion(full_version_string string) ([]int, error) {
151160
}
152161

153162
// Register root container before running this function!
154-
func Register(factory info.MachineInfoFactory) error {
163+
func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo) error {
155164
client, err := docker.NewClient(*ArgDockerEndpoint)
156165
if err != nil {
157166
return fmt.Errorf("unable to communicate with docker daemon: %v", err)
@@ -213,6 +222,7 @@ func Register(factory info.MachineInfoFactory) error {
213222
client: client,
214223
usesAufsDriver: usesAufsDriver,
215224
cgroupSubsystems: cgroupSubsystems,
225+
fsInfo: fsInfo,
216226
}
217227
container.RegisterContainerHandlerFactory(f)
218228
return nil

container/docker/handler.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,16 +82,11 @@ func newDockerContainerHandler(
8282
client *docker.Client,
8383
name string,
8484
machineInfoFactory info.MachineInfoFactory,
85+
fsInfo fs.FsInfo,
8586
dockerRootDir string,
8687
usesAufsDriver bool,
8788
cgroupSubsystems *containerLibcontainer.CgroupSubsystems,
8889
) (container.ContainerHandler, error) {
89-
// TODO(vmarmol): Get from factory.
90-
fsInfo, err := fs.NewFsInfo()
91-
if err != nil {
92-
return nil, err
93-
}
94-
9590
// Create the cgroup paths.
9691
cgroupPaths := make(map[string]string, len(cgroupSubsystems.MountPoints))
9792
for key, val := range cgroupSubsystems.MountPoints {

container/raw/factory.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/golang/glog"
2121
"github.com/google/cadvisor/container"
2222
"github.com/google/cadvisor/container/libcontainer"
23+
"github.com/google/cadvisor/fs"
2324
info "github.com/google/cadvisor/info/v1"
2425
)
2526

@@ -29,22 +30,25 @@ type rawFactory struct {
2930

3031
// Information about the cgroup subsystems.
3132
cgroupSubsystems *libcontainer.CgroupSubsystems
33+
34+
// Information about mounted filesystems.
35+
fsInfo fs.FsInfo
3236
}
3337

3438
func (self *rawFactory) String() string {
3539
return "raw"
3640
}
3741

3842
func (self *rawFactory) NewContainerHandler(name string) (container.ContainerHandler, error) {
39-
return newRawContainerHandler(name, self.cgroupSubsystems, self.machineInfoFactory)
43+
return newRawContainerHandler(name, self.cgroupSubsystems, self.machineInfoFactory, self.fsInfo)
4044
}
4145

4246
// The raw factory can handle any container.
4347
func (self *rawFactory) CanHandle(name string) (bool, error) {
4448
return true, nil
4549
}
4650

47-
func Register(machineInfoFactory info.MachineInfoFactory) error {
51+
func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo) error {
4852
cgroupSubsystems, err := libcontainer.GetCgroupSubsystems()
4953
if err != nil {
5054
return fmt.Errorf("failed to get cgroup subsystems: %v", err)
@@ -56,6 +60,7 @@ func Register(machineInfoFactory info.MachineInfoFactory) error {
5660
glog.Infof("Registering Raw factory")
5761
factory := &rawFactory{
5862
machineInfoFactory: machineInfoFactory,
63+
fsInfo: fsInfo,
5964
cgroupSubsystems: &cgroupSubsystems,
6065
}
6166
container.RegisterContainerHandlerFactory(factory)

container/raw/handler.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,18 +71,13 @@ type rawContainerHandler struct {
7171
externalMounts []mount
7272
}
7373

74-
func newRawContainerHandler(name string, cgroupSubsystems *libcontainer.CgroupSubsystems, machineInfoFactory info.MachineInfoFactory) (container.ContainerHandler, error) {
74+
func newRawContainerHandler(name string, cgroupSubsystems *libcontainer.CgroupSubsystems, machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo) (container.ContainerHandler, error) {
7575
// Create the cgroup paths.
7676
cgroupPaths := make(map[string]string, len(cgroupSubsystems.MountPoints))
7777
for key, val := range cgroupSubsystems.MountPoints {
7878
cgroupPaths[key] = path.Join(val, name)
7979
}
8080

81-
// TODO(vmarmol): Get from factory.
82-
fsInfo, err := fs.NewFsInfo()
83-
if err != nil {
84-
return nil, err
85-
}
8681
cHints, err := getContainerHintsFromFile(*argContainerHints)
8782
if err != nil {
8883
return nil, err

fs/fs.go

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"os"
3030
"os/exec"
3131
"path"
32+
"path/filepath"
3233
"regexp"
3334
"strconv"
3435
"strings"
@@ -41,22 +42,38 @@ import (
4142

4243
var partitionRegex = regexp.MustCompile("^(:?(:?s|xv)d[a-z]+\\d*|dm-\\d+)$")
4344

45+
const (
46+
LabelSystemRoot = "root"
47+
LabelDockerImages = "docker-images"
48+
)
49+
4450
type partition struct {
4551
mountpoint string
4652
major uint
4753
minor uint
4854
}
4955

5056
type RealFsInfo struct {
57+
// Map from block device path to partition information.
5158
partitions map[string]partition
59+
// Map from label to block device path.
60+
// Labels are intent-specific tags that are auto-detected.
61+
labels map[string]string
62+
}
63+
64+
type Context struct {
65+
// docker root directory.
66+
DockerRoot string
5267
}
5368

54-
func NewFsInfo() (FsInfo, error) {
69+
func NewFsInfo(context Context) (FsInfo, error) {
5570
mounts, err := mount.GetMounts()
5671
if err != nil {
5772
return nil, err
5873
}
5974
partitions := make(map[string]partition, 0)
75+
fsInfo := &RealFsInfo{}
76+
fsInfo.labels = make(map[string]string, 0)
6077
for _, mount := range mounts {
6178
if !strings.HasPrefix(mount.Fstype, "ext") && mount.Fstype != "btrfs" {
6279
continue
@@ -68,7 +85,84 @@ func NewFsInfo() (FsInfo, error) {
6885
partitions[mount.Source] = partition{mount.Mountpoint, uint(mount.Major), uint(mount.Minor)}
6986
}
7087
glog.Infof("Filesystem partitions: %+v", partitions)
71-
return &RealFsInfo{partitions}, nil
88+
fsInfo.partitions = partitions
89+
fsInfo.addLabels(context)
90+
return fsInfo, nil
91+
}
92+
93+
func (self *RealFsInfo) addLabels(context Context) {
94+
dockerPaths := getDockerImagePaths(context)
95+
for src, p := range self.partitions {
96+
if p.mountpoint == "/" {
97+
if _, ok := self.labels[LabelSystemRoot]; !ok {
98+
self.labels[LabelSystemRoot] = src
99+
}
100+
}
101+
self.updateDockerImagesPath(src, p.mountpoint, dockerPaths)
102+
// TODO(rjnagal): Add label for docker devicemapper pool.
103+
}
104+
}
105+
106+
// Generate a list of possible mount points for docker image management from the docker root directory.
107+
// Right now, we look for each type of supported graph driver directories, but we can do better by parsing
108+
// some of the context from `docker info`.
109+
func getDockerImagePaths(context Context) []string {
110+
// TODO(rjnagal): Detect docker root and graphdriver directories from docker info.
111+
dockerRoot := context.DockerRoot
112+
dockerImagePaths := []string{}
113+
for _, dir := range []string{"devicemapper", "btrfs", "aufs"} {
114+
dockerImagePaths = append(dockerImagePaths, path.Join(dockerRoot, dir))
115+
}
116+
for dockerRoot != "/" && dockerRoot != "." {
117+
dockerImagePaths = append(dockerImagePaths, dockerRoot)
118+
dockerRoot = filepath.Dir(dockerRoot)
119+
}
120+
dockerImagePaths = append(dockerImagePaths, "/")
121+
return dockerImagePaths
122+
}
123+
124+
// This method compares the mountpoint with possible docker image mount points. If a match is found,
125+
// docker images label is added to the partition.
126+
func (self *RealFsInfo) updateDockerImagesPath(source string, mountpoint string, dockerImagePaths []string) {
127+
for _, v := range dockerImagePaths {
128+
if v == mountpoint {
129+
if i, ok := self.labels[LabelDockerImages]; ok {
130+
// pick the innermost mountpoint.
131+
mnt := self.partitions[i].mountpoint
132+
if len(mnt) < len(mountpoint) {
133+
self.labels[LabelDockerImages] = source
134+
}
135+
} else {
136+
self.labels[LabelDockerImages] = source
137+
}
138+
}
139+
}
140+
}
141+
142+
func (self *RealFsInfo) GetDeviceForLabel(label string) (string, error) {
143+
dev, ok := self.labels[label]
144+
if !ok {
145+
return "", fmt.Errorf("non-existent label %q", label)
146+
}
147+
return dev, nil
148+
}
149+
150+
func (self *RealFsInfo) GetLabelsForDevice(device string) ([]string, error) {
151+
labels := []string{}
152+
for label, dev := range self.labels {
153+
if dev == device {
154+
labels = append(labels, label)
155+
}
156+
}
157+
return labels, nil
158+
}
159+
160+
func (self *RealFsInfo) GetMountpointForDevice(dev string) (string, error) {
161+
p, ok := self.partitions[dev]
162+
if !ok {
163+
return "", fmt.Errorf("no partition info for device %q", dev)
164+
}
165+
return p.mountpoint, nil
72166
}
73167

74168
func (self *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, error) {

fs/types.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,13 @@ type FsInfo interface {
5353

5454
// Returns the block device info of the filesystem on which 'dir' resides.
5555
GetDirFsDevice(dir string) (*DeviceInfo, error)
56+
57+
// Returns the device name associated with a particular label.
58+
GetDeviceForLabel(label string) (string, error)
59+
60+
// Returns all labels associated with a particular device name.
61+
GetLabelsForDevice(device string) ([]string, error)
62+
63+
// Returns the mountpoint associated with a particular device.
64+
GetMountpointForDevice(device string) (string, error)
5665
}

info/v2/container.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,20 @@ type DerivedStats struct {
9797
// Percentile in last day.
9898
DayUsage Usage `json:"day_usage"`
9999
}
100+
101+
type FsInfo struct {
102+
// The block device name associated with the filesystem.
103+
Device string `json:"device"`
104+
105+
// Path where the filesystem is mounted.
106+
Mountpoint string `json:"mountpoint"`
107+
108+
// Filesystem usage in bytes.
109+
Capacity uint64 `json:"capacity"`
110+
111+
// Number of bytes used on this filesystem.
112+
Usage uint64 `json:"usage"`
113+
114+
// Labels associated with this filesystem.
115+
Labels []string `json:"labels"`
116+
}

manager/machine.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ func getMachineID() string {
223223
return ""
224224
}
225225

226-
func getMachineInfo(sysFs sysfs.SysFs) (*info.MachineInfo, error) {
226+
func getMachineInfo(sysFs sysfs.SysFs, fsInfo fs.FsInfo) (*info.MachineInfo, error) {
227227
cpuinfo, err := ioutil.ReadFile("/proc/cpuinfo")
228228
clockSpeed, err := getClockSpeed(cpuinfo)
229229
if err != nil {
@@ -241,10 +241,6 @@ func getMachineInfo(sysFs sysfs.SysFs) (*info.MachineInfo, error) {
241241
return nil, err
242242
}
243243

244-
fsInfo, err := fs.NewFsInfo()
245-
if err != nil {
246-
return nil, err
247-
}
248244
filesystems, err := fsInfo.GetGlobalFsInfo()
249245
if err != nil {
250246
return nil, err

0 commit comments

Comments
 (0)