Merge pull request #368 from rjnagal/diskinfo

Add a disk map to machine info.
This commit is contained in:
Victor Marmol 2014-12-17 08:21:07 +08:00
commit cc0e999bed
8 changed files with 224 additions and 8 deletions

View File

@ -34,6 +34,7 @@ import (
"github.com/google/cadvisor/manager"
"github.com/google/cadvisor/pages"
"github.com/google/cadvisor/pages/static"
"github.com/google/cadvisor/utils/sysfs"
"github.com/google/cadvisor/validate"
)
@ -65,7 +66,12 @@ func main() {
glog.Fatalf("Failed to connect to database: %s", err)
}
containerManager, err := manager.New(storageDriver)
sysFs, err := sysfs.NewRealSysFs()
if err != nil {
glog.Fatalf("Failed to create a system interface: %s", err)
}
containerManager, err := manager.New(storageDriver, sysFs)
if err != nil {
glog.Fatalf("Failed to create a Container Manager: %s", err)
}

View File

@ -58,7 +58,7 @@ func cadvisorTestClient(path string, expectedPostObj, expectedPostObjEmpty, repl
encoder := json.NewEncoder(w)
encoder.Encode(replyObj)
} else if r.URL.Path == "/api/v1.2/machine" {
fmt.Fprint(w, `{"num_cores":8,"memory_capacity":31625871360}`)
fmt.Fprint(w, `{"num_cores":8,"memory_capacity":31625871360, "disk_map":["8:0":{"name":"sda","major":8,"minor":0,"size":10737418240}]}`)
} else {
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "Page not found.")
@ -78,6 +78,14 @@ func TestGetMachineinfo(t *testing.T) {
minfo := &info.MachineInfo{
NumCores: 8,
MemoryCapacity: 31625871360,
DiskMap: map[string]info.DiskInfo{
"8:0": info.DiskInfo{
Name: "sda",
Major: 8,
Minor: 0,
Size: 10737418240,
},
},
}
client, server, err := cadvisorTestClient("/api/v1.2/machine", nil, nil, minfo, t)
if err != nil {

View File

@ -22,6 +22,20 @@ type FsInfo struct {
Capacity uint64 `json:"capacity"`
}
type DiskInfo struct {
// device name
Name string `json:"name"`
// Major number
Major uint64 `json:"major"`
// Minor number
Minor uint64 `json:"minor"`
// Size in bytes
Size uint64 `json:"size"`
}
type MachineInfo struct {
// The number of cores in this machine.
NumCores int `json:"num_cores"`
@ -31,6 +45,9 @@ type MachineInfo struct {
// Filesystems on this machine.
Filesystems []FsInfo `json:"filesystems"`
// Disk map
DiskMap map[string]DiskInfo `json:"disk_map"`
}
type VersionInfo struct {

View File

@ -27,12 +27,13 @@ import (
"github.com/google/cadvisor/container/docker"
"github.com/google/cadvisor/fs"
"github.com/google/cadvisor/info"
"github.com/google/cadvisor/utils/sysfs"
)
var numCpuRegexp = regexp.MustCompile("processor\\t*: +[0-9]+")
var memoryCapacityRegexp = regexp.MustCompile("MemTotal: *([0-9]+) kB")
func getMachineInfo() (*info.MachineInfo, error) {
func getMachineInfo(sysFs sysfs.SysFs) (*info.MachineInfo, error) {
// Get the number of CPUs from /proc/cpuinfo.
out, err := ioutil.ReadFile("/proc/cpuinfo")
if err != nil {
@ -69,10 +70,17 @@ func getMachineInfo() (*info.MachineInfo, error) {
return nil, err
}
diskMap, err := sysfs.GetBlockDeviceInfo(sysFs)
if err != nil {
return nil, err
}
machineInfo := &info.MachineInfo{
NumCores: numCores,
MemoryCapacity: memoryCapacity,
DiskMap: diskMap,
}
for _, fs := range filesystems {
machineInfo.Filesystems = append(machineInfo.Filesystems, info.FsInfo{fs.Device, fs.Capacity})
}

View File

@ -30,6 +30,7 @@ import (
"github.com/google/cadvisor/container/docker"
"github.com/google/cadvisor/info"
"github.com/google/cadvisor/storage"
"github.com/google/cadvisor/utils/sysfs"
)
var globalHousekeepingInterval = flag.Duration("global_housekeeping_interval", 1*time.Minute, "Interval between global housekeepings")
@ -64,7 +65,7 @@ type Manager interface {
}
// New takes a driver and returns a new manager.
func New(driver storage.StorageDriver) (Manager, error) {
func New(driver storage.StorageDriver, sysfs sysfs.SysFs) (Manager, error) {
if driver == nil {
return nil, fmt.Errorf("nil storage driver!")
}
@ -83,7 +84,7 @@ func New(driver storage.StorageDriver) (Manager, error) {
cadvisorContainer: selfContainer,
}
machineInfo, err := getMachineInfo()
machineInfo, err := getMachineInfo(sysfs)
if err != nil {
return nil, err
}

View File

@ -27,12 +27,14 @@ import (
"github.com/google/cadvisor/info"
itest "github.com/google/cadvisor/info/test"
stest "github.com/google/cadvisor/storage/test"
"github.com/google/cadvisor/utils/sysfs/fakesysfs"
)
// TODO(vmarmol): Refactor these tests.
func createManagerAndAddContainers(
driver *stest.MockStorageDriver,
sysfs *fakesysfs.FakeSysFs,
containers []string,
f func(*container.MockContainerHandler),
t *testing.T,
@ -41,7 +43,7 @@ func createManagerAndAddContainers(
driver = &stest.MockStorageDriver{}
}
container.ClearContainerHandlerFactories()
mif, err := New(driver)
mif, err := New(driver, sysfs)
if err != nil {
t.Fatal(err)
}
@ -81,8 +83,10 @@ func expectManagerWithContainers(containers []string, query *info.ContainerInfoR
}
driver := &stest.MockStorageDriver{}
sysfs := &fakesysfs.FakeSysFs{}
m := createManagerAndAddContainers(
driver,
sysfs,
containers,
func(h *container.MockContainerHandler) {
cinfo := infosMap[h.Name]
@ -200,7 +204,7 @@ func TestDockerContainersInfo(t *testing.T) {
}
func TestNew(t *testing.T) {
manager, err := New(&stest.MockStorageDriver{})
manager, err := New(&stest.MockStorageDriver{}, &fakesysfs.FakeSysFs{})
if err != nil {
t.Fatalf("Expected manager.New to succeed: %s", err)
}
@ -210,7 +214,7 @@ func TestNew(t *testing.T) {
}
func TestNewNilManager(t *testing.T) {
_, err := New(nil)
_, err := New(nil, nil)
if err == nil {
t.Fatalf("Expected nil manager to return error")
}

View File

@ -0,0 +1,64 @@
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fakesysfs
import (
"os"
"time"
)
// If we extend sysfs to support more interfaces, it might be worth making this a mock instead of a fake.
type FileInfo struct {
}
func (self *FileInfo) Name() string {
return "sda"
}
func (self *FileInfo) Size() int64 {
return 1234567
}
func (self *FileInfo) Mode() os.FileMode {
return 0
}
func (self *FileInfo) ModTime() time.Time {
return time.Time{}
}
func (self *FileInfo) IsDir() bool {
return true
}
func (self *FileInfo) Sys() interface{} {
return nil
}
type FakeSysFs struct {
info FileInfo
}
func (self *FakeSysFs) GetBlockDevices() ([]os.FileInfo, error) {
return []os.FileInfo{&self.info}, nil
}
func (self *FakeSysFs) GetBlockDeviceSize(name string) (string, error) {
return "1234567", nil
}
func (self *FakeSysFs) GetBlockDeviceNumbers(name string) (string, error) {
return "8:0\n", nil
}

108
utils/sysfs/sysfs.go Normal file
View File

@ -0,0 +1,108 @@
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package sysfs
import (
"fmt"
"io/ioutil"
"os"
"path"
"strconv"
"strings"
"github.com/google/cadvisor/info"
)
const BlockDir = "/sys/block"
// Abstracts the lowest level calls to sysfs.
type SysFs interface {
// Get directory information for available block devices.
GetBlockDevices() ([]os.FileInfo, error)
// Get Size of a given block device.
GetBlockDeviceSize(string) (string, error)
// Get device major:minor number string.
GetBlockDeviceNumbers(string) (string, error)
}
type realSysFs struct{}
func NewRealSysFs() (SysFs, error) {
return &realSysFs{}, nil
}
func (self *realSysFs) GetBlockDevices() ([]os.FileInfo, error) {
return ioutil.ReadDir(BlockDir)
}
func (self *realSysFs) GetBlockDeviceNumbers(name string) (string, error) {
dev, err := ioutil.ReadFile(path.Join(BlockDir, name, "/dev"))
if err != nil {
return "", err
}
return string(dev), nil
}
func (self *realSysFs) GetBlockDeviceSize(name string) (string, error) {
size, err := ioutil.ReadFile(path.Join(BlockDir, name, "/size"))
if err != nil {
return "", err
}
return string(size), nil
}
// Get information about block devices present on the system.
// Uses the passed in system interface to retrieve the low level OS information.
func GetBlockDeviceInfo(sysfs SysFs) (map[string]info.DiskInfo, error) {
disks, err := sysfs.GetBlockDevices()
if err != nil {
return nil, err
}
diskMap := make(map[string]info.DiskInfo)
for _, disk := range disks {
name := disk.Name()
// Ignore loopback and ram devices.
if strings.HasPrefix(name, "loop") || strings.HasPrefix(name, "ram") {
continue
}
disk_info := info.DiskInfo{
Name: name,
}
dev, err := sysfs.GetBlockDeviceNumbers(name)
if err != nil {
return nil, err
}
n, err := fmt.Sscanf(dev, "%d:%d", &disk_info.Major, &disk_info.Minor)
if err != nil || n != 2 {
return nil, fmt.Errorf("could not parse device numbers from %s for device %s", dev, name)
}
out, err := sysfs.GetBlockDeviceSize(name)
if err != nil {
return nil, err
}
// Remove trailing newline before conversion.
size, err := strconv.ParseUint(strings.TrimSpace(out), 10, 64)
if err != nil {
return nil, err
}
// size is in 512 bytes blocks.
disk_info.Size = size * 512
device := fmt.Sprintf("%d:%d", disk_info.Major, disk_info.Minor)
diskMap[device] = disk_info
}
return diskMap, nil
}