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)), + } +}