Skip to content
1 change: 1 addition & 0 deletions client/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ type Config struct {
// determined dynamically.
DiskTotalMB int

// DEPRECATED: Remove in Nomad 1.13.0. Use Reserved.Disk instead.
// DiskFreeMB is the default node free disk space in megabytes if it cannot be
// determined dynamically.
DiskFreeMB int
Expand Down
12 changes: 6 additions & 6 deletions client/fingerprint/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,30 +39,30 @@ func (f *StorageFingerprint) Fingerprint(req *FingerprintRequest, resp *Fingerpr
}
}

volume, total, free, err := f.diskFree(storageDir)
volume, total, err := f.diskInfo(storageDir)
if err != nil {
return fmt.Errorf("failed to determine disk space for %s: %v", storageDir, err)
}

if cfg.DiskTotalMB > 0 {
total = uint64(cfg.DiskTotalMB) * bytesPerMegabyte
}

free := total - uint64(req.Node.ReservedResources.Disk.DiskMB)

// DEPRECATED: remove in 1.13.0
if cfg.DiskFreeMB > 0 {
free = uint64(cfg.DiskFreeMB) * bytesPerMegabyte
}

if total < free {
return fmt.Errorf("detected more free disk space (%d) than total disk space (%d), use disk_total_mb and disk_free_mb to correct", free, total)
}

resp.AddAttribute("unique.storage.volume", volume)
resp.AddAttribute("unique.storage.bytestotal", strconv.FormatUint(total, 10))
resp.AddAttribute("unique.storage.bytesfree", strconv.FormatUint(free, 10))

// set the disk size for the response
resp.NodeResources = &structs.NodeResources{
Disk: structs.NodeDiskResources{
DiskMB: int64(free / bytesPerMegabyte),
DiskMB: int64(total / bytesPerMegabyte),
},
}
resp.Detected = true
Expand Down
25 changes: 9 additions & 16 deletions client/fingerprint/storage_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ import (
"strings"
)

// diskFree inspects the filesystem for path and returns the volume name and
// the total and free bytes available on the file system.
func (f *StorageFingerprint) diskFree(path string) (volume string, total, free uint64, err error) {
// diskInfo inspects the filesystem for path and returns the volume name and
// the total bytes available on the file system.
func (f *StorageFingerprint) diskInfo(path string) (volume string, total uint64, err error) {
absPath, err := filepath.Abs(path)
if err != nil {
return "", 0, 0, fmt.Errorf("failed to determine absolute path for %s", path)
return "", 0, fmt.Errorf("failed to determine absolute path for %s", path)
}

// Use -k to standardize the output values between darwin and linux
Expand All @@ -34,35 +34,28 @@ func (f *StorageFingerprint) diskFree(path string) (volume string, total, free u

mountOutput, err := exec.Command("df", dfArgs, absPath).Output()
if err != nil {
return "", 0, 0, fmt.Errorf("failed to determine mount point for %s", absPath)
return "", 0, fmt.Errorf("failed to determine mount point for %s", absPath)
}
// Output looks something like:
// Filesystem 1024-blocks Used Available Capacity iused ifree %iused Mounted on
// /dev/disk1 487385240 423722532 63406708 87% 105994631 15851677 87% /
// [0] volume [1] capacity [2] SKIP [3] free
lines := strings.Split(string(mountOutput), "\n")
if len(lines) < 2 {
return "", 0, 0, fmt.Errorf("failed to parse `df` output; expected at least 2 lines")
return "", 0, fmt.Errorf("failed to parse `df` output; expected at least 2 lines")
}
fields := strings.Fields(lines[1])
if len(fields) < 4 {
return "", 0, 0, fmt.Errorf("failed to parse `df` output; expected at least 4 columns")
return "", 0, fmt.Errorf("failed to parse `df` output; expected at least 4 columns")
}
volume = fields[0]

total, err = strconv.ParseUint(fields[1], 10, 64)
if err != nil {
return "", 0, 0, fmt.Errorf("failed to parse storage.bytestotal size in kilobytes")
return "", 0, fmt.Errorf("failed to parse storage.bytestotal size in kilobytes")
}
// convert to bytes
total *= 1024

free, err = strconv.ParseUint(fields[3], 10, 64)
if err != nil {
return "", 0, 0, fmt.Errorf("failed to parse storage.bytesfree size in kilobytes")
}
// convert to bytes
free *= 1024

return volume, total, free, nil
return volume, total, nil
}
20 changes: 10 additions & 10 deletions client/fingerprint/storage_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,28 @@ import (
"syscall"
)

//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zstorage_windows.go storage_windows.go
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zstorage_windows.go storage_windows.go

//sys getDiskFreeSpaceEx(dirName *uint16, availableFreeBytes *uint64, totalBytes *uint64, totalFreeBytes *uint64) (err error) = kernel32.GetDiskFreeSpaceExW
//sys getDiskSpaceEx(dirName *uint16, availableFreeBytes *uint64, totalBytes *uint64, totalFreeBytes *uint64) (err error) = kernel32.GetDiskFreeSpaceExW

// diskFree inspects the filesystem for path and returns the volume name and
// the total and free bytes available on the file system.
func (f *StorageFingerprint) diskFree(path string) (volume string, total, free uint64, err error) {
// diskInfo inspects the filesystem for path and returns the volume name and
// the total bytes available on the file system.
func (f *StorageFingerprint) diskInfo(path string) (volume string, total uint64, err error) {
absPath, err := filepath.Abs(path)
if err != nil {
return "", 0, 0, fmt.Errorf("failed to determine absolute path for %s", path)
return "", 0, fmt.Errorf("failed to determine absolute path for %s", path)
}

volume = filepath.VolumeName(absPath)

absPathp, err := syscall.UTF16PtrFromString(absPath)
if err != nil {
return "", 0, 0, fmt.Errorf("failed to convert \"%s\" to UTF16: %v", absPath, err)
return "", 0, fmt.Errorf("failed to convert \"%s\" to UTF16: %v", absPath, err)
}

if err := getDiskFreeSpaceEx(absPathp, nil, &total, &free); err != nil {
return "", 0, 0, fmt.Errorf("failed to get free disk space for %s: %v", absPath, err)
if err := getDiskSpaceEx(absPathp, nil, &total, nil); err != nil {
return "", 0, fmt.Errorf("failed to get free disk space for %s: %v", absPath, err)
}

return volume, total, free, nil
return volume, total, nil
}
42 changes: 33 additions & 9 deletions client/fingerprint/zstorage_windows.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions command/agent/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ type ClientConfig struct {
// DiskTotalMB is used to override any detected or default total disk space.
DiskTotalMB int `hcl:"disk_total_mb"`

// DEPRECATED: Remove in Nomad 1.13.0. Use Reserved.Disk instead.
// DiskFreeMB is used to override any detected or default free disk space.
DiskFreeMB int `hcl:"disk_free_mb"`

Expand Down
3 changes: 2 additions & 1 deletion website/content/docs/configuration/client.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ client {

- `disk_free_mb` `(int:0)` - Specifies the disk space free for scheduling
allocations. If set, this value overrides any detected free disk space. This
value can be seen in `nomad node status` under Allocated Resources.
value can be seen in `nomad node status` under Allocated Resources. Deprecated -
please use client.reserved.disk instead.

- `min_dynamic_port` `(int:20000)` - Specifies the minimum dynamic port to be
assigned. Individual ports and ranges of ports may be excluded from dynamic
Expand Down
8 changes: 8 additions & 0 deletions website/content/docs/upgrade/upgrade-specific.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ used to document those details separately from the standard upgrade flow.

## Nomad 1.11.0

#### Storage fingerprinting has been simplified

Nomad will now calculate the storage available for scheduling using only
`totalBytes - client.reserved.disk`. As such, the `client.disk_free_mb` field
is now ignored. Nomad will also no longer attempt to calculate the free disk
space, as this changes when clients with running allocations restart. Users are
recommended to reserve at least the amount of disk that is used by the host OS.

#### Sysbatch jobs will no longer accept `reschedule` blocks

In Nomad 1.11.0, submitting a sysbatch job with a `reschedule` block returns
Expand Down
Loading