Skip to content

Commit

Permalink
Add an API to get FsStats from filesystem UUID
Browse files Browse the repository at this point in the history
  • Loading branch information
yguo0905 committed Aug 23, 2017
1 parent a0be0ea commit a5adaad
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 26 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ sudo docker run \
--volume=/var/run:/var/run:rw \
--volume=/sys:/sys:ro \
--volume=/var/lib/docker/:/var/lib/docker:ro \
--volume=/dev/disk/:/dev/disk:ro \
--publish=8080:8080 \
--detach=true \
--name=cadvisor \
Expand Down
23 changes: 15 additions & 8 deletions api/versions.go
Original file line number Diff line number Diff line change
Expand Up @@ -420,23 +420,30 @@ func (self *version2_0) HandleRequest(requestType string, request []string, m ma
}
return writeResult(specs, w)
case storageApi:
var err error
fi := []v2.FsInfo{}
label := r.URL.Query().Get("label")
if len(label) == 0 {
// Get all global filesystems info.
fi, err = m.GetFsInfo("")
uuid := r.URL.Query().Get("uuid")
switch {
case uuid != "":
fi, err := m.GetFsInfoByFsUUID(uuid)
if err != nil {
return err
}
} else {
return writeResult(fi, w)
case label != "":
// Get a specific label.
fi, err = m.GetFsInfo(label)
fi, err := m.GetFsInfo(label)
if err != nil {
return err
}
return writeResult(fi, w)
default:
// Get all global filesystems info.
fi, err := m.GetFsInfo("")
if err != nil {
return err
}
return writeResult(fi, w)
}
return writeResult(fi, w)
case eventsApi:
return handleEventRequest(request, m, w, r)
case psApi:
Expand Down
61 changes: 57 additions & 4 deletions fs/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ type RealFsInfo struct {
mounts map[string]*mount.Info
// devicemapper client
dmsetup devicemapper.DmsetupClient
// fsUUIDToDeviceName is a map from the filesystem UUID to its device name.
fsUUIDToDeviceName map[string]string
}

type Context struct {
Expand All @@ -103,13 +105,19 @@ func NewFsInfo(context Context) (FsInfo, error) {
return nil, err
}

fsUUIDToDeviceName, err := getFsUUIDToDeviceNameMap()
if err != nil {
return nil, err
}

// Avoid devicemapper container mounts - these are tracked by the ThinPoolWatcher
excluded := []string{fmt.Sprintf("%s/devicemapper/mnt", context.Docker.Root)}
fsInfo := &RealFsInfo{
partitions: processMounts(mounts, excluded),
labels: make(map[string]string, 0),
mounts: make(map[string]*mount.Info, 0),
dmsetup: devicemapper.NewDmsetupClient(),
partitions: processMounts(mounts, excluded),
labels: make(map[string]string, 0),
mounts: make(map[string]*mount.Info, 0),
dmsetup: devicemapper.NewDmsetupClient(),
fsUUIDToDeviceName: fsUUIDToDeviceName,
}

for _, mount := range mounts {
Expand All @@ -121,11 +129,44 @@ func NewFsInfo(context Context) (FsInfo, error) {
// add a "partition" for devicemapper to fsInfo.partitions
fsInfo.addDockerImagesLabel(context, mounts)

glog.Infof("Filesystem UUIDs: %+v", fsInfo.fsUUIDToDeviceName)
glog.Infof("Filesystem partitions: %+v", fsInfo.partitions)
fsInfo.addSystemRootLabel(mounts)
return fsInfo, nil
}

// getFsUUIDToDeviceNameMap creates the filesystem uuid to device name map
// using the information in /dev/disk/by-uuid. If the directory does not exist,
// this function will return an empty map.
func getFsUUIDToDeviceNameMap() (map[string]string, error) {
const dir = "/dev/disk/by-uuid"

if _, err := os.Stat(dir); os.IsNotExist(err) {
return make(map[string]string), nil
}

files, err := ioutil.ReadDir(dir)
if err != nil {
return nil, err
}

fsUUIDToDeviceName := make(map[string]string)
for _, file := range files {
path := filepath.Join(dir, file.Name())
target, err := os.Readlink(path)
if err != nil {
glog.Infof("Failed to resolve symlink for %q", path)
continue
}
device, err := filepath.Abs(filepath.Join(dir, target))
if err != nil {
return nil, fmt.Errorf("failed to resolve the absolute path of %q", filepath.Join(dir, target))
}
fsUUIDToDeviceName[file.Name()] = device
}
return fsUUIDToDeviceName, nil
}

func processMounts(mounts []*mount.Info, excludedMountpointPrefixes []string) map[string]partition {
partitions := make(map[string]partition, 0)

Expand Down Expand Up @@ -433,6 +474,18 @@ func minor(devNumber uint64) uint {
return uint((devNumber & 0xff) | ((devNumber >> 12) & 0xfff00))
}

func (self *RealFsInfo) GetDeviceInfoByFsUUID(uuid string) (*DeviceInfo, error) {
deviceName, found := self.fsUUIDToDeviceName[uuid]
if !found {
return nil, ErrNoSuchDevice
}
p, found := self.partitions[deviceName]
if !found {
return nil, fmt.Errorf("cannot find device %q in partitions", deviceName)
}
return &DeviceInfo{deviceName, p.major, p.minor}, nil
}

func (self *RealFsInfo) GetDirFsDevice(dir string) (*DeviceInfo, error) {
buf := new(syscall.Stat_t)
err := syscall.Stat(dir, buf)
Expand Down
13 changes: 12 additions & 1 deletion fs/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@

package fs

import "time"
import (
"errors"
"time"
)

type DeviceInfo struct {
Device string
Expand Down Expand Up @@ -59,6 +62,9 @@ type DiskStats struct {
WeightedIoTime uint64
}

// ErrNoSuchDevice is the error indicating the requested device does not exist.
var ErrNoSuchDevice = errors.New("cadvisor: no such device")

type FsInfo interface {
// Returns capacity and free space, in bytes, of all the ext2, ext3, ext4 filesystems on the host.
GetGlobalFsInfo() ([]Fs, error)
Expand All @@ -72,6 +78,11 @@ type FsInfo interface {
// Returns number of inodes used by 'dir'.
GetDirInodeUsage(dir string, timeout time.Duration) (uint64, error)

// GetDeviceInfoByFsUUID returns the information of the device with the
// specified filesystem uuid. If no such device exists, this function will
// return the ErrNoSuchDevice error.
GetDeviceInfoByFsUUID(uuid string) (*DeviceInfo, error)

// Returns the block device info of the filesystem on which 'dir' resides.
GetDirFsDevice(dir string) (*DeviceInfo, error)

Expand Down
3 changes: 3 additions & 0 deletions info/v2/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ type DerivedStats struct {
}

type FsInfo struct {
// Time of generation of these stats.
Timestamp time.Time `json:"timestamp"`

// The block device name associated with the filesystem.
Device string `json:"device"`

Expand Down
44 changes: 31 additions & 13 deletions manager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ type Manager interface {
// Get version information about different components we depend on.
GetVersionInfo() (*info.VersionInfo, error)

// GetFsInfoByFsUUID returns the information of the device having the
// specified filesystem uuid. If no such device with the UUID exists, this
// function will return the fs.ErrNoSuchDevice error.
GetFsInfoByFsUUID(uuid string) (v2.FsInfo, error)

// Get filesystem information for the filesystem that contains the given directory
GetDirFsInfo(dir string) (v2.FsInfo, error)

Expand Down Expand Up @@ -676,24 +681,19 @@ func (self *manager) getRequestedContainers(containerName string, options v2.Req
}

func (self *manager) GetDirFsInfo(dir string) (v2.FsInfo, error) {
dirDevice, err := self.fsInfo.GetDirFsDevice(dir)
if err != nil {
return v2.FsInfo{}, fmt.Errorf("error trying to get filesystem Device for dir %v: err: %v", dir, err)
}
dirMountpoint, err := self.fsInfo.GetMountpointForDevice(dirDevice.Device)
device, err := self.fsInfo.GetDirFsDevice(dir)
if err != nil {
return v2.FsInfo{}, fmt.Errorf("error trying to get MountPoint for Root Device: %v, err: %v", dirDevice, err)
return v2.FsInfo{}, fmt.Errorf("failed to get device for dir %q: %v", dir, err)
}
infos, err := self.GetFsInfo("")
return self.getFsInfoByDeviceName(device.Device)
}

func (self *manager) GetFsInfoByFsUUID(uuid string) (v2.FsInfo, error) {
device, err := self.fsInfo.GetDeviceInfoByFsUUID(uuid)
if err != nil {
return v2.FsInfo{}, err
}
for _, info := range infos {
if info.Mountpoint == dirMountpoint {
return info, nil
}
}
return v2.FsInfo{}, fmt.Errorf("did not find fs info for dir: %v", dir)
return self.getFsInfoByDeviceName(device.Device)
}

func (self *manager) GetFsInfo(label string) ([]v2.FsInfo, error) {
Expand Down Expand Up @@ -726,6 +726,7 @@ func (self *manager) GetFsInfo(label string) ([]v2.FsInfo, error) {
}

fi := v2.FsInfo{
Timestamp: stats[0].Timestamp,
Device: fs.Device,
Mountpoint: mountpoint,
Capacity: fs.Limit,
Expand Down Expand Up @@ -1265,6 +1266,23 @@ func (m *manager) DebugInfo() map[string][]string {
return debugInfo
}

func (self *manager) getFsInfoByDeviceName(deviceName string) (v2.FsInfo, error) {
mountPoint, err := self.fsInfo.GetMountpointForDevice(deviceName)
if err != nil {
return v2.FsInfo{}, fmt.Errorf("failed to get mount point for device %q: %v", deviceName, err)
}
infos, err := self.GetFsInfo("")
if err != nil {
return v2.FsInfo{}, err
}
for _, info := range infos {
if info.Mountpoint == mountPoint {
return info, nil
}
}
return v2.FsInfo{}, fmt.Errorf("cannot find filesystem info for device %q", deviceName)
}

func getVersionInfo() (*info.VersionInfo, error) {

kernel_version := machine.KernelVersion()
Expand Down

0 comments on commit a5adaad

Please sign in to comment.