Merge pull request #1724 from yguo0905/uuid
Add an API to get FsStats from filesystem UUID
This commit is contained in:
commit
81635163ce
@ -16,6 +16,7 @@ sudo docker run \
|
|||||||
--volume=/var/run:/var/run:rw \
|
--volume=/var/run:/var/run:rw \
|
||||||
--volume=/sys:/sys:ro \
|
--volume=/sys:/sys:ro \
|
||||||
--volume=/var/lib/docker/:/var/lib/docker:ro \
|
--volume=/var/lib/docker/:/var/lib/docker:ro \
|
||||||
|
--volume=/dev/disk/:/dev/disk:ro \
|
||||||
--publish=8080:8080 \
|
--publish=8080:8080 \
|
||||||
--detach=true \
|
--detach=true \
|
||||||
--name=cadvisor \
|
--name=cadvisor \
|
||||||
|
@ -420,23 +420,30 @@ func (self *version2_0) HandleRequest(requestType string, request []string, m ma
|
|||||||
}
|
}
|
||||||
return writeResult(specs, w)
|
return writeResult(specs, w)
|
||||||
case storageApi:
|
case storageApi:
|
||||||
var err error
|
|
||||||
fi := []v2.FsInfo{}
|
|
||||||
label := r.URL.Query().Get("label")
|
label := r.URL.Query().Get("label")
|
||||||
if len(label) == 0 {
|
uuid := r.URL.Query().Get("uuid")
|
||||||
// Get all global filesystems info.
|
switch {
|
||||||
fi, err = m.GetFsInfo("")
|
case uuid != "":
|
||||||
|
fi, err := m.GetFsInfoByFsUUID(uuid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
return writeResult(fi, w)
|
||||||
|
case label != "":
|
||||||
// Get a specific label.
|
// Get a specific label.
|
||||||
fi, err = m.GetFsInfo(label)
|
fi, err := m.GetFsInfo(label)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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:
|
case eventsApi:
|
||||||
return handleEventRequest(request, m, w, r)
|
return handleEventRequest(request, m, w, r)
|
||||||
case psApi:
|
case psApi:
|
||||||
|
61
fs/fs.go
61
fs/fs.go
@ -83,6 +83,8 @@ type RealFsInfo struct {
|
|||||||
mounts map[string]*mount.Info
|
mounts map[string]*mount.Info
|
||||||
// devicemapper client
|
// devicemapper client
|
||||||
dmsetup devicemapper.DmsetupClient
|
dmsetup devicemapper.DmsetupClient
|
||||||
|
// fsUUIDToDeviceName is a map from the filesystem UUID to its device name.
|
||||||
|
fsUUIDToDeviceName map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Context struct {
|
type Context struct {
|
||||||
@ -103,13 +105,19 @@ func NewFsInfo(context Context) (FsInfo, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fsUUIDToDeviceName, err := getFsUUIDToDeviceNameMap()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Avoid devicemapper container mounts - these are tracked by the ThinPoolWatcher
|
// Avoid devicemapper container mounts - these are tracked by the ThinPoolWatcher
|
||||||
excluded := []string{fmt.Sprintf("%s/devicemapper/mnt", context.Docker.Root)}
|
excluded := []string{fmt.Sprintf("%s/devicemapper/mnt", context.Docker.Root)}
|
||||||
fsInfo := &RealFsInfo{
|
fsInfo := &RealFsInfo{
|
||||||
partitions: processMounts(mounts, excluded),
|
partitions: processMounts(mounts, excluded),
|
||||||
labels: make(map[string]string, 0),
|
labels: make(map[string]string, 0),
|
||||||
mounts: make(map[string]*mount.Info, 0),
|
mounts: make(map[string]*mount.Info, 0),
|
||||||
dmsetup: devicemapper.NewDmsetupClient(),
|
dmsetup: devicemapper.NewDmsetupClient(),
|
||||||
|
fsUUIDToDeviceName: fsUUIDToDeviceName,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, mount := range mounts {
|
for _, mount := range mounts {
|
||||||
@ -121,11 +129,44 @@ func NewFsInfo(context Context) (FsInfo, error) {
|
|||||||
// add a "partition" for devicemapper to fsInfo.partitions
|
// add a "partition" for devicemapper to fsInfo.partitions
|
||||||
fsInfo.addDockerImagesLabel(context, mounts)
|
fsInfo.addDockerImagesLabel(context, mounts)
|
||||||
|
|
||||||
|
glog.Infof("Filesystem UUIDs: %+v", fsInfo.fsUUIDToDeviceName)
|
||||||
glog.Infof("Filesystem partitions: %+v", fsInfo.partitions)
|
glog.Infof("Filesystem partitions: %+v", fsInfo.partitions)
|
||||||
fsInfo.addSystemRootLabel(mounts)
|
fsInfo.addSystemRootLabel(mounts)
|
||||||
return fsInfo, nil
|
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 {
|
func processMounts(mounts []*mount.Info, excludedMountpointPrefixes []string) map[string]partition {
|
||||||
partitions := make(map[string]partition, 0)
|
partitions := make(map[string]partition, 0)
|
||||||
|
|
||||||
@ -433,6 +474,18 @@ func minor(devNumber uint64) uint {
|
|||||||
return uint((devNumber & 0xff) | ((devNumber >> 12) & 0xfff00))
|
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) {
|
func (self *RealFsInfo) GetDirFsDevice(dir string) (*DeviceInfo, error) {
|
||||||
buf := new(syscall.Stat_t)
|
buf := new(syscall.Stat_t)
|
||||||
err := syscall.Stat(dir, buf)
|
err := syscall.Stat(dir, buf)
|
||||||
|
13
fs/types.go
13
fs/types.go
@ -14,7 +14,10 @@
|
|||||||
|
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type DeviceInfo struct {
|
type DeviceInfo struct {
|
||||||
Device string
|
Device string
|
||||||
@ -59,6 +62,9 @@ type DiskStats struct {
|
|||||||
WeightedIoTime uint64
|
WeightedIoTime uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrNoSuchDevice is the error indicating the requested device does not exist.
|
||||||
|
var ErrNoSuchDevice = errors.New("cadvisor: no such device")
|
||||||
|
|
||||||
type FsInfo interface {
|
type FsInfo interface {
|
||||||
// Returns capacity and free space, in bytes, of all the ext2, ext3, ext4 filesystems on the host.
|
// Returns capacity and free space, in bytes, of all the ext2, ext3, ext4 filesystems on the host.
|
||||||
GetGlobalFsInfo() ([]Fs, error)
|
GetGlobalFsInfo() ([]Fs, error)
|
||||||
@ -72,6 +78,11 @@ type FsInfo interface {
|
|||||||
// Returns number of inodes used by 'dir'.
|
// Returns number of inodes used by 'dir'.
|
||||||
GetDirInodeUsage(dir string, timeout time.Duration) (uint64, error)
|
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.
|
// Returns the block device info of the filesystem on which 'dir' resides.
|
||||||
GetDirFsDevice(dir string) (*DeviceInfo, error)
|
GetDirFsDevice(dir string) (*DeviceInfo, error)
|
||||||
|
|
||||||
|
@ -199,6 +199,9 @@ type DerivedStats struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type FsInfo struct {
|
type FsInfo struct {
|
||||||
|
// Time of generation of these stats.
|
||||||
|
Timestamp time.Time `json:"timestamp"`
|
||||||
|
|
||||||
// The block device name associated with the filesystem.
|
// The block device name associated with the filesystem.
|
||||||
Device string `json:"device"`
|
Device string `json:"device"`
|
||||||
|
|
||||||
|
@ -101,6 +101,11 @@ type Manager interface {
|
|||||||
// Get version information about different components we depend on.
|
// Get version information about different components we depend on.
|
||||||
GetVersionInfo() (*info.VersionInfo, error)
|
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
|
// Get filesystem information for the filesystem that contains the given directory
|
||||||
GetDirFsInfo(dir string) (v2.FsInfo, error)
|
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) {
|
func (self *manager) GetDirFsInfo(dir string) (v2.FsInfo, error) {
|
||||||
dirDevice, err := self.fsInfo.GetDirFsDevice(dir)
|
device, err := self.fsInfo.GetDirFsDevice(dir)
|
||||||
if err != nil {
|
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)
|
return self.getFsInfoByDeviceName(device.Device)
|
||||||
if err != nil {
|
}
|
||||||
return v2.FsInfo{}, fmt.Errorf("error trying to get MountPoint for Root Device: %v, err: %v", dirDevice, err)
|
|
||||||
}
|
func (self *manager) GetFsInfoByFsUUID(uuid string) (v2.FsInfo, error) {
|
||||||
infos, err := self.GetFsInfo("")
|
device, err := self.fsInfo.GetDeviceInfoByFsUUID(uuid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return v2.FsInfo{}, err
|
return v2.FsInfo{}, err
|
||||||
}
|
}
|
||||||
for _, info := range infos {
|
return self.getFsInfoByDeviceName(device.Device)
|
||||||
if info.Mountpoint == dirMountpoint {
|
|
||||||
return info, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return v2.FsInfo{}, fmt.Errorf("did not find fs info for dir: %v", dir)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *manager) GetFsInfo(label string) ([]v2.FsInfo, error) {
|
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{
|
fi := v2.FsInfo{
|
||||||
|
Timestamp: stats[0].Timestamp,
|
||||||
Device: fs.Device,
|
Device: fs.Device,
|
||||||
Mountpoint: mountpoint,
|
Mountpoint: mountpoint,
|
||||||
Capacity: fs.Limit,
|
Capacity: fs.Limit,
|
||||||
@ -1265,6 +1266,23 @@ func (m *manager) DebugInfo() map[string][]string {
|
|||||||
return debugInfo
|
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) {
|
func getVersionInfo() (*info.VersionInfo, error) {
|
||||||
|
|
||||||
kernel_version := machine.KernelVersion()
|
kernel_version := machine.KernelVersion()
|
||||||
|
Loading…
Reference in New Issue
Block a user