Add a disk map to machine info.

This is read once at start of cAdvisor. We can use this to report
machine state as well as return logical name for block devices in UI.

Signed-off-by: Rohit Jnagal <jnagal@google.com> (github: rjnagal)
This commit is contained in:
Rohit Jnagal 2014-09-23 06:28:11 +00:00
parent 5ad9cb5bb5
commit e5200948f5
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
}