From 34e9ad24aade4c6942a550f1e3a799794409a982 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Thu, 26 May 2016 14:13:10 -0700 Subject: [PATCH] XL: Introduce new API StorageInfo. (#1770) This is necessary for calculating the total storage capacity from object layer. This value is also needed for browser UI. Buckets used to carry this information, this patch deprecates this feature. --- fs-v1.go | 25 +++++++++++++++++-------- object-datatypes.go | 8 ++++++-- object-interface.go | 3 +++ posix.go | 24 ------------------------ storage-datatypes.go | 3 --- web-handlers.go | 22 ++++++++++++++++------ xl-v1.go | 38 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 80 insertions(+), 43 deletions(-) diff --git a/fs-v1.go b/fs-v1.go index f4ab2a060..824347d88 100644 --- a/fs-v1.go +++ b/fs-v1.go @@ -25,29 +25,31 @@ import ( "strings" "sync" + "github.com/minio/minio/pkg/disk" "github.com/minio/minio/pkg/mimedb" ) // fsObjects - Implements fs object layer. type fsObjects struct { storage StorageAPI + physicalDisk string listObjectMap map[listParams][]*treeWalkerFS listObjectMapMutex *sync.Mutex } // newFSObjects - initialize new fs object layer. -func newFSObjects(exportPath string) (ObjectLayer, error) { +func newFSObjects(disk string) (ObjectLayer, error) { var storage StorageAPI var err error - if !strings.ContainsRune(exportPath, ':') || filepath.VolumeName(exportPath) != "" { + if !strings.ContainsRune(disk, ':') || filepath.VolumeName(disk) != "" { // Initialize filesystem storage API. - storage, err = newPosix(exportPath) + storage, err = newPosix(disk) if err != nil { return nil, err } } else { // Initialize rpc client storage API. - storage, err = newRPCClient(exportPath) + storage, err = newRPCClient(disk) if err != nil { return nil, err } @@ -60,11 +62,22 @@ func newFSObjects(exportPath string) (ObjectLayer, error) { // Return successfully initialized object layer. return fsObjects{ storage: storage, + physicalDisk: disk, listObjectMap: make(map[listParams][]*treeWalkerFS), listObjectMapMutex: &sync.Mutex{}, }, nil } +// StorageInfo - returns underlying storage statistics. +func (fs fsObjects) StorageInfo() StorageInfo { + info, err := disk.GetInfo(fs.physicalDisk) + fatalIf(err, "Unable to get disk info "+fs.physicalDisk) + return StorageInfo{ + Total: info.Total, + Free: info.Free, + } +} + /// Bucket operations // MakeBucket - make a bucket. @@ -92,8 +105,6 @@ func (fs fsObjects) GetBucketInfo(bucket string) (BucketInfo, error) { return BucketInfo{ Name: bucket, Created: vi.Created, - Total: vi.Total, - Free: vi.Free, }, nil } @@ -113,8 +124,6 @@ func (fs fsObjects) ListBuckets() ([]BucketInfo, error) { bucketInfos = append(bucketInfos, BucketInfo{ Name: vol.Name, Created: vol.Created, - Total: vol.Total, - Free: vol.Free, }) } sort.Sort(byBucketName(bucketInfos)) diff --git a/object-datatypes.go b/object-datatypes.go index a893dfd8d..f25e8c978 100644 --- a/object-datatypes.go +++ b/object-datatypes.go @@ -18,12 +18,16 @@ package main import "time" +// StorageInfo - represents total capacity of underlying storage. +type StorageInfo struct { + Total int64 // Total disk space. + Free int64 // Free total available disk space. +} + // BucketInfo - bucket name and create date type BucketInfo struct { Name string Created time.Time - Total int64 - Free int64 } // ObjectInfo - object info. diff --git a/object-interface.go b/object-interface.go index 6c6731892..891fcd616 100644 --- a/object-interface.go +++ b/object-interface.go @@ -20,6 +20,9 @@ import "io" // ObjectLayer implements primitives for object API layer. type ObjectLayer interface { + // Storage operations. + StorageInfo() StorageInfo + // Bucket operations. MakeBucket(bucket string) error GetBucketInfo(bucket string) (bucketInfo BucketInfo, err error) diff --git a/posix.go b/posix.go index fe4240b35..a16b97a7d 100644 --- a/posix.go +++ b/posix.go @@ -202,15 +202,6 @@ func (s fsStorage) MakeVol(volume string) (err error) { // ListVols - list volumes. func (s fsStorage) ListVols() (volsInfo []VolInfo, err error) { - // Get disk info to be populated for VolInfo. - var diskInfo disk.Info - diskInfo, err = disk.GetInfo(s.diskPath) - if err != nil { - if os.IsNotExist(err) { - return nil, errDiskNotFound - } - return nil, err - } volsInfo, err = listVols(s.diskPath) if err != nil { return nil, err @@ -219,9 +210,6 @@ func (s fsStorage) ListVols() (volsInfo []VolInfo, err error) { volInfo := VolInfo{ Name: vol.Name, Created: vol.Created, - Total: diskInfo.Total, - Free: diskInfo.Free, - FSType: diskInfo.FSType, } volsInfo[i] = volInfo } @@ -244,24 +232,12 @@ func (s fsStorage) StatVol(volume string) (volInfo VolInfo, err error) { } return VolInfo{}, err } - // Get disk info, to be returned back along with volume info. - var diskInfo disk.Info - diskInfo, err = disk.GetInfo(s.diskPath) - if err != nil { - if os.IsNotExist(err) { - return VolInfo{}, errDiskNotFound - } - return VolInfo{}, err - } // As os.Stat() doesn't carry other than ModTime(), use ModTime() // as CreatedTime. createdTime := st.ModTime() return VolInfo{ Name: volume, Created: createdTime, - Free: diskInfo.Free, - Total: diskInfo.Total, - FSType: diskInfo.FSType, }, nil } diff --git a/storage-datatypes.go b/storage-datatypes.go index c963f3e59..353a7a8e3 100644 --- a/storage-datatypes.go +++ b/storage-datatypes.go @@ -25,9 +25,6 @@ import ( type VolInfo struct { Name string Created time.Time - Total int64 - Free int64 - FSType string } // FileInfo - file stat information. diff --git a/web-handlers.go b/web-handlers.go index 909eeecc7..ffd32bb9f 100644 --- a/web-handlers.go +++ b/web-handlers.go @@ -98,6 +98,22 @@ func (web *webAPIHandlers) ServerInfo(r *http.Request, args *WebGenericArgs, rep return nil } +// StorageInfoRep - contains storage usage statistics. +type StorageInfoRep struct { + StorageInfo StorageInfo `json:"storageInfo"` + UIVersion string `json:"uiVersion"` +} + +// StorageInfo - web call to gather storage usage statistics. +func (web *webAPIHandlers) StorageInfo(r *http.Request, args *GenericArgs, reply *StorageInfoRep) error { + if !isJWTReqAuthenticated(r) { + return &json2.Error{Message: "Unauthorized request"} + } + reply.UIVersion = miniobrowser.UIVersion + reply.StorageInfo = web.ObjectAPI.StorageInfo() + return nil +} + // MakeBucketArgs - make bucket args. type MakeBucketArgs struct { BucketName string `json:"bucketName"` @@ -127,10 +143,6 @@ type WebBucketInfo struct { Name string `json:"name"` // Date the bucket was created. CreationDate time.Time `json:"creationDate"` - // Total storage space where the bucket resides. - Total int64 `json:"total"` - // Free storage space where the bucket resides. - Free int64 `json:"free"` } // ListBuckets - list buckets api. @@ -148,8 +160,6 @@ func (web *webAPIHandlers) ListBuckets(r *http.Request, args *WebGenericArgs, re reply.Buckets = append(reply.Buckets, WebBucketInfo{ Name: bucket.Name, CreationDate: bucket.Created, - Total: bucket.Total, - Free: bucket.Free, }) } } diff --git a/xl-v1.go b/xl-v1.go index 7d120bef4..e55e5a48a 100644 --- a/xl-v1.go +++ b/xl-v1.go @@ -20,8 +20,11 @@ import ( "errors" "fmt" "path/filepath" + "sort" "strings" "sync" + + "github.com/minio/minio/pkg/disk" ) const ( @@ -33,6 +36,7 @@ const ( // xlObjects - Implements fs object layer. type xlObjects struct { storageDisks []StorageAPI + physicalDisks []string dataBlocks int parityBlocks int readQuorum int @@ -147,6 +151,7 @@ func newXLObjects(disks []string) (ObjectLayer, error) { xl := xlObjects{ storageDisks: newPosixDisks, + physicalDisks: disks, dataBlocks: dataBlocks, parityBlocks: parityBlocks, listObjectMap: make(map[listParams][]*treeWalker), @@ -168,3 +173,36 @@ func newXLObjects(disks []string) (ObjectLayer, error) { // Return successfully initialized object layer. return xl, nil } + +// byDiskTotal is a collection satisfying sort.Interface. +type byDiskTotal []disk.Info + +func (d byDiskTotal) Len() int { return len(d) } +func (d byDiskTotal) Swap(i, j int) { d[i], d[j] = d[j], d[i] } +func (d byDiskTotal) Less(i, j int) bool { + return d[i].Total < d[j].Total +} + +// StorageInfo - returns underlying storage statistics. +func (xl xlObjects) StorageInfo() StorageInfo { + var disksInfo []disk.Info + for _, diskPath := range xl.physicalDisks { + info, err := disk.GetInfo(diskPath) + if err != nil { + errorIf(err, "Unable to fetch disk info for "+diskPath) + continue + } + disksInfo = append(disksInfo, info) + } + + // Sort so that the first element is the smallest. + sort.Sort(byDiskTotal(disksInfo)) + + // Return calculated storage info, choose the lowest Total and + // Free as the total aggregated values. Total capacity is always + // the multiple of smallest disk among the disk list. + return StorageInfo{ + Total: disksInfo[0].Total * int64(len(xl.storageDisks)), + Free: disksInfo[0].Free * int64(len(xl.storageDisks)), + } +}