|
|
@ -35,9 +35,11 @@ import ( |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
const ( |
|
|
|
const ( |
|
|
|
fsMinFreeSpace = 1 * humanize.GiByte // Min 1GiB free space.
|
|
|
|
diskMinFreeSpace = 1 * humanize.GiByte // Min 1GiB free space.
|
|
|
|
fsMinFreeInodes = 10000 // Min 10000.
|
|
|
|
diskMinTotalSpace = diskMinFreeSpace // Min 1GiB total space.
|
|
|
|
maxAllowedIOError = 5 |
|
|
|
diskMinFreeInodes = 10000 // Min 10000 free inodes.
|
|
|
|
|
|
|
|
diskMinTotalInodes = diskMinFreeInodes // Min 10000 total inodes.
|
|
|
|
|
|
|
|
maxAllowedIOError = 5 |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
// posix - implements StorageAPI interface.
|
|
|
|
// posix - implements StorageAPI interface.
|
|
|
@ -108,7 +110,7 @@ func newPosix(path string) (StorageAPI, error) { |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
fs := &posix{ |
|
|
|
st := &posix{ |
|
|
|
diskPath: diskPath, |
|
|
|
diskPath: diskPath, |
|
|
|
// 1MiB buffer pool for posix internal operations.
|
|
|
|
// 1MiB buffer pool for posix internal operations.
|
|
|
|
pool: sync.Pool{ |
|
|
|
pool: sync.Pool{ |
|
|
@ -131,7 +133,19 @@ func newPosix(path string) (StorageAPI, error) { |
|
|
|
return nil, err |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return fs, nil |
|
|
|
|
|
|
|
|
|
|
|
di, err := getDiskInfo(preparePath(diskPath)) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check if disk has minimum required total space.
|
|
|
|
|
|
|
|
if err = checkDiskMinTotal(di); err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Success.
|
|
|
|
|
|
|
|
return st, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// getDiskInfo returns given disk information.
|
|
|
|
// getDiskInfo returns given disk information.
|
|
|
@ -155,44 +169,77 @@ var ignoreDiskFreeOS = []string{ |
|
|
|
globalSolarisOSName, |
|
|
|
globalSolarisOSName, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// checkDiskFree verifies if disk path has sufficient minimum free disk space and files.
|
|
|
|
// check if disk total has minimum required size.
|
|
|
|
func checkDiskFree(diskPath string, neededSpace int64) (err error) { |
|
|
|
func checkDiskMinTotal(di disk.Info) (err error) { |
|
|
|
// We don't validate disk space or inode utilization on windows.
|
|
|
|
// Remove 5% from total space for cumulative disk space
|
|
|
|
// Each windows calls to 'GetVolumeInformationW' takes around 3-5seconds.
|
|
|
|
// used for journalling, inodes etc.
|
|
|
|
// And StatFS is not supported by Go for solaris and netbsd.
|
|
|
|
totalDiskSpace := float64(di.Total) * 0.95 |
|
|
|
if contains(ignoreDiskFreeOS, runtime.GOOS) { |
|
|
|
if int64(totalDiskSpace) <= diskMinTotalSpace { |
|
|
|
return nil |
|
|
|
return errDiskFull |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var di disk.Info |
|
|
|
// Some filesystems do not implement a way to provide total inodes available, instead
|
|
|
|
di, err = getDiskInfo(preparePath(diskPath)) |
|
|
|
// inodes are allocated based on available disk space. For example CephDISK, StoreNext CVDISK,
|
|
|
|
if err != nil { |
|
|
|
// AzureFile driver. Allow for the available disk to be separately validated and we will
|
|
|
|
return err |
|
|
|
// validate inodes only if total inodes are provided by the underlying filesystem.
|
|
|
|
|
|
|
|
if di.Files != 0 && di.FSType != "NFS" { |
|
|
|
|
|
|
|
totalFiles := int64(di.Files) |
|
|
|
|
|
|
|
if totalFiles <= diskMinTotalInodes { |
|
|
|
|
|
|
|
return errDiskFull |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// check if disk free has minimum required size.
|
|
|
|
|
|
|
|
func checkDiskMinFree(di disk.Info) error { |
|
|
|
// Remove 5% from free space for cumulative disk space used for journalling, inodes etc.
|
|
|
|
// Remove 5% from free space for cumulative disk space used for journalling, inodes etc.
|
|
|
|
availableDiskSpace := float64(di.Free) * 0.95 |
|
|
|
availableDiskSpace := float64(di.Free) * 0.95 |
|
|
|
if int64(availableDiskSpace) <= fsMinFreeSpace { |
|
|
|
if int64(availableDiskSpace) <= diskMinFreeSpace { |
|
|
|
return errDiskFull |
|
|
|
return errDiskFull |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Some filesystems do not implement a way to provide total inodes available, instead inodes
|
|
|
|
// Some filesystems do not implement a way to provide total inodes available, instead inodes
|
|
|
|
// are allocated based on available disk space. For example CephFS, StoreNext CVFS, AzureFile driver.
|
|
|
|
// are allocated based on available disk space. For example CephDISK, StoreNext CVDISK, AzureFile driver.
|
|
|
|
// Allow for the available disk to be separately validate and we will validate inodes only if
|
|
|
|
// Allow for the available disk to be separately validate and we will validate inodes only if
|
|
|
|
// total inodes are provided by the underlying filesystem.
|
|
|
|
// total inodes are provided by the underlying filesystem.
|
|
|
|
if di.Files != 0 && di.FSType != "NFS" { |
|
|
|
if di.Files != 0 && di.FSType != "NFS" { |
|
|
|
availableFiles := int64(di.Ffree) |
|
|
|
availableFiles := int64(di.Ffree) |
|
|
|
if availableFiles <= fsMinFreeInodes { |
|
|
|
if availableFiles <= diskMinFreeInodes { |
|
|
|
return errDiskFull |
|
|
|
return errDiskFull |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Success.
|
|
|
|
|
|
|
|
return nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// checkDiskFree verifies if disk path has sufficient minimum free disk space and files.
|
|
|
|
|
|
|
|
func checkDiskFree(diskPath string, neededSpace int64) (err error) { |
|
|
|
|
|
|
|
// We don't validate disk space or inode utilization on windows.
|
|
|
|
|
|
|
|
// Each windows call to 'GetVolumeInformationW' takes around
|
|
|
|
|
|
|
|
// 3-5seconds. And StatDISK is not supported by Go for solaris
|
|
|
|
|
|
|
|
// and netbsd.
|
|
|
|
|
|
|
|
if contains(ignoreDiskFreeOS, runtime.GOOS) { |
|
|
|
|
|
|
|
return nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var di disk.Info |
|
|
|
|
|
|
|
di, err = getDiskInfo(preparePath(diskPath)) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if err = checkDiskMinFree(di); err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Check if we have enough space to store data
|
|
|
|
// Check if we have enough space to store data
|
|
|
|
if neededSpace > int64(availableDiskSpace) { |
|
|
|
if neededSpace > int64(float64(di.Free)*0.95) { |
|
|
|
return errDiskFull |
|
|
|
return errDiskFull |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Success.
|
|
|
|
|
|
|
|
return nil |
|
|
|
return nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|