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:
parent
5ad9cb5bb5
commit
e5200948f5
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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})
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
64
utils/sysfs/fakesysfs/fake.go
Normal file
64
utils/sysfs/fakesysfs/fake.go
Normal 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
108
utils/sysfs/sysfs.go
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user