Merge pull request #1724 from yguo0905/uuid

Add an API to get FsStats from filesystem UUID
This commit is contained in:
David Ashpole 2017-08-23 12:43:08 -07:00 committed by GitHub
commit 81635163ce
6 changed files with 119 additions and 26 deletions

View File

@ -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 \

View File

@ -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:

View File

@ -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 {
@ -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 {
@ -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)
@ -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)

View File

@ -14,7 +14,10 @@
package fs
import "time"
import (
"errors"
"time"
)
type DeviceInfo struct {
Device string
@ -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)
@ -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)

View File

@ -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"`

View File

@ -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)
@ -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)
device, 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)
return v2.FsInfo{}, fmt.Errorf("failed to get device for dir %q: %v", dir, err)
}
dirMountpoint, err := self.fsInfo.GetMountpointForDevice(dirDevice.Device)
if err != nil {
return v2.FsInfo{}, fmt.Errorf("error trying to get MountPoint for Root Device: %v, err: %v", dirDevice, 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) {
@ -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,
@ -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()