From bf17eae7c30c7cc7c5608687b684cc662a4dea02 Mon Sep 17 00:00:00 2001 From: Mahmood Ali Date: Thu, 6 Dec 2018 07:10:22 -0500 Subject: [PATCH 1/5] devices/nvidia: memory state as the summary stat --- devices/gpu/nvidia/stats.go | 2 +- devices/gpu/nvidia/stats_test.go | 180 +++++++++++++++++-------------- 2 files changed, 103 insertions(+), 79 deletions(-) diff --git a/devices/gpu/nvidia/stats.go b/devices/gpu/nvidia/stats.go index 6bbcd10e1..529b4e3c0 100644 --- a/devices/gpu/nvidia/stats.go +++ b/devices/gpu/nvidia/stats.go @@ -286,7 +286,7 @@ func statsForItem(statsItem *nvml.StatsData, timestamp time.Time) *device.Device } } return &device.DeviceStats{ - Summary: temperatureStat, + Summary: memoryStateStat, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ PowerUsageAttr: powerUsageStat, diff --git a/devices/gpu/nvidia/stats_test.go b/devices/gpu/nvidia/stats_test.go index 2c7d31559..f6221e0f4 100644 --- a/devices/gpu/nvidia/stats_test.go +++ b/devices/gpu/nvidia/stats_test.go @@ -447,9 +447,10 @@ func TestStatsForItem(t *testing.T) { }, ExpectedResult: &device.DeviceStats{ Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(1), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(1), + IntDenominatorVal: helper.Int64ToPtr(1), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -541,9 +542,10 @@ func TestStatsForItem(t *testing.T) { }, ExpectedResult: &device.DeviceStats{ Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(1), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(1), + IntDenominatorVal: helper.Int64ToPtr(1), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -634,9 +636,10 @@ func TestStatsForItem(t *testing.T) { }, ExpectedResult: &device.DeviceStats{ Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(1), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(1), + IntDenominatorVal: helper.Int64ToPtr(1), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -727,9 +730,10 @@ func TestStatsForItem(t *testing.T) { }, ExpectedResult: &device.DeviceStats{ Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(1), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(1), + IntDenominatorVal: helper.Int64ToPtr(1), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -821,9 +825,10 @@ func TestStatsForItem(t *testing.T) { }, ExpectedResult: &device.DeviceStats{ Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(1), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(1), + IntDenominatorVal: helper.Int64ToPtr(1), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -915,9 +920,10 @@ func TestStatsForItem(t *testing.T) { }, ExpectedResult: &device.DeviceStats{ Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(1), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(1), + IntDenominatorVal: helper.Int64ToPtr(1), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -1009,9 +1015,10 @@ func TestStatsForItem(t *testing.T) { }, ExpectedResult: &device.DeviceStats{ Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(1), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(1), + IntDenominatorVal: helper.Int64ToPtr(1), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -1103,9 +1110,10 @@ func TestStatsForItem(t *testing.T) { }, ExpectedResult: &device.DeviceStats{ Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - StringVal: helper.StringToPtr(notAvailable), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(1), + IntDenominatorVal: helper.Int64ToPtr(1), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -1197,9 +1205,9 @@ func TestStatsForItem(t *testing.T) { }, ExpectedResult: &device.DeviceStats{ Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(1), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + StringVal: helper.StringToPtr(notAvailable), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -1290,9 +1298,9 @@ func TestStatsForItem(t *testing.T) { }, ExpectedResult: &device.DeviceStats{ Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(1), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + StringVal: helper.StringToPtr(notAvailable), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -1383,9 +1391,10 @@ func TestStatsForItem(t *testing.T) { }, ExpectedResult: &device.DeviceStats{ Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(1), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(1), + IntDenominatorVal: helper.Int64ToPtr(1), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -1476,9 +1485,10 @@ func TestStatsForItem(t *testing.T) { }, ExpectedResult: &device.DeviceStats{ Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(1), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(1), + IntDenominatorVal: helper.Int64ToPtr(1), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -1569,9 +1579,10 @@ func TestStatsForItem(t *testing.T) { }, ExpectedResult: &device.DeviceStats{ Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(1), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(1), + IntDenominatorVal: helper.Int64ToPtr(1), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -1663,9 +1674,10 @@ func TestStatsForItem(t *testing.T) { }, ExpectedResult: &device.DeviceStats{ Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(1), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(1), + IntDenominatorVal: helper.Int64ToPtr(1), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -1757,9 +1769,10 @@ func TestStatsForItem(t *testing.T) { }, ExpectedResult: &device.DeviceStats{ Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(1), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(1), + IntDenominatorVal: helper.Int64ToPtr(1), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -1913,9 +1926,10 @@ func TestStatsForGroup(t *testing.T) { InstanceStats: map[string]*device.DeviceStats{ "UUID1": { Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(1), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(1), + IntDenominatorVal: helper.Int64ToPtr(1), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -1983,9 +1997,10 @@ func TestStatsForGroup(t *testing.T) { }, "UUID2": { Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(2), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(2), + IntDenominatorVal: helper.Int64ToPtr(2), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -2053,9 +2068,10 @@ func TestStatsForGroup(t *testing.T) { }, "UUID3": { Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(3), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(3), + IntDenominatorVal: helper.Int64ToPtr(3), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -2234,9 +2250,10 @@ func TestWriteStatsToChannel(t *testing.T) { InstanceStats: map[string]*device.DeviceStats{ "UUID1": { Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(1), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(1), + IntDenominatorVal: helper.Int64ToPtr(1), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -2311,9 +2328,10 @@ func TestWriteStatsToChannel(t *testing.T) { InstanceStats: map[string]*device.DeviceStats{ "UUID2": { Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(2), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(2), + IntDenominatorVal: helper.Int64ToPtr(2), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -2388,9 +2406,10 @@ func TestWriteStatsToChannel(t *testing.T) { InstanceStats: map[string]*device.DeviceStats{ "UUID3": { Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(3), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(3), + IntDenominatorVal: helper.Int64ToPtr(3), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -2545,9 +2564,10 @@ func TestWriteStatsToChannel(t *testing.T) { InstanceStats: map[string]*device.DeviceStats{ "UUID1": { Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(1), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(1), + IntDenominatorVal: helper.Int64ToPtr(1), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -2622,9 +2642,10 @@ func TestWriteStatsToChannel(t *testing.T) { InstanceStats: map[string]*device.DeviceStats{ "UUID3": { Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(3), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(3), + IntDenominatorVal: helper.Int64ToPtr(3), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -2692,9 +2713,10 @@ func TestWriteStatsToChannel(t *testing.T) { }, "UUID2": { Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(2), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(2), + IntDenominatorVal: helper.Int64ToPtr(2), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -2848,9 +2870,10 @@ func TestWriteStatsToChannel(t *testing.T) { InstanceStats: map[string]*device.DeviceStats{ "UUID1": { Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(1), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(1), + IntDenominatorVal: helper.Int64ToPtr(1), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ @@ -2925,9 +2948,10 @@ func TestWriteStatsToChannel(t *testing.T) { InstanceStats: map[string]*device.DeviceStats{ "UUID2": { Summary: &structs.StatValue{ - Unit: TemperatureUnit, - Desc: TemperatureDesc, - IntNumeratorVal: helper.Int64ToPtr(2), + Unit: MemoryStateUnit, + Desc: MemoryStateDesc, + IntNumeratorVal: helper.Int64ToPtr(2), + IntDenominatorVal: helper.Int64ToPtr(2), }, Stats: &structs.StatObject{ Attributes: map[string]*structs.StatValue{ From 612f79bd454491fc5e2a36e940df82105d3d3f80 Mon Sep 17 00:00:00 2001 From: Mahmood Ali Date: Mon, 10 Dec 2018 10:33:34 -0500 Subject: [PATCH 2/5] Rename helper_stats -> helper_devices --- command/{helper_stats.go => helper_devices.go} | 0 command/{helper_stats_test.go => helper_devices_test.go} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename command/{helper_stats.go => helper_devices.go} (100%) rename command/{helper_stats_test.go => helper_devices_test.go} (100%) diff --git a/command/helper_stats.go b/command/helper_devices.go similarity index 100% rename from command/helper_stats.go rename to command/helper_devices.go diff --git a/command/helper_stats_test.go b/command/helper_devices_test.go similarity index 100% rename from command/helper_stats_test.go rename to command/helper_devices_test.go From a802984c9ad9d3b7523f76771fe217c600597ce3 Mon Sep 17 00:00:00 2001 From: Mahmood Ali Date: Mon, 10 Dec 2018 11:51:43 -0500 Subject: [PATCH 3/5] device attributes in `nomad node status -verbose` This reports device attributes like the following: ``` $ nomad node status -self -verbose ID = f7adb958-29e1-2a5a-2303-9d61ffaab33a Name = mars.local Class = DC = dc1 Drain = false Eligibility = eligible Status = ready Uptime = 12h40m13s Drivers Driver Detected Healthy Message Time docker true true healthy 2018-12-10T11:47:19-05:00 ... Attributes cpu.arch = amd64 cpu.frequency = 2200 cpu.modelname = Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz cpu.numcores = 12 ... Device Group Attributes Device Group = nomad/file/mock block_device = sda1 filesystem = ext4 size = 63.2 GB Meta ``` --- api/nodes.go | 1 - api/resources.go | 41 +++++++++++++++++++++++++++++----- command/helper_devices.go | 12 ++++++++++ command/helper_devices_test.go | 26 +++++++++++++++++++++ command/node_status.go | 27 ++++++++++++++++++++++ 5 files changed, 101 insertions(+), 6 deletions(-) diff --git a/api/nodes.go b/api/nodes.go index 74381cb83..bfed5d8eb 100644 --- a/api/nodes.go +++ b/api/nodes.go @@ -683,7 +683,6 @@ func (v *StatValue) String() string { } return str case v.IntNumeratorVal != nil: - str := strconv.FormatInt(*v.IntNumeratorVal, 10) if v.IntDenominatorVal != nil { str += " / " + strconv.FormatInt(*v.IntDenominatorVal, 10) diff --git a/api/resources.go b/api/resources.go index e21387769..767d82625 100644 --- a/api/resources.go +++ b/api/resources.go @@ -1,6 +1,10 @@ package api -import "github.com/hashicorp/nomad/helper" +import ( + "strconv" + + "github.com/hashicorp/nomad/helper" +) // Resources encapsulates the required resources of // a given task or task group. @@ -125,6 +129,10 @@ type NodeDeviceResource struct { Attributes map[string]*Attribute } +func (r NodeDeviceResource) ID() string { + return r.Vendor + "/" + r.Type + "/" + r.Name +} + // NodeDevice is an instance of a particular device. type NodeDevice struct { // ID is the ID of the device. @@ -146,21 +154,44 @@ type NodeDevice struct { // specifying units type Attribute struct { // Float is the float value for the attribute - Float *float64 + FloatVal *float64 `json:"Float,omitempty"` // Int is the int value for the attribute - Int *int64 + IntVal *int64 `json:"Int,omitempty"` // String is the string value for the attribute - String *string + StringVal *string `json:"String,omitempty"` // Bool is the bool value for the attribute - Bool *bool + BoolVal *bool `json:"Bool,omitempty"` // Unit is the optional unit for the set int or float value Unit string } +func (a Attribute) String() string { + switch { + case a.FloatVal != nil: + str := strconv.FormatFloat(*a.FloatVal, 'f', -1, 64) + if a.Unit != "" { + str += " " + a.Unit + } + return str + case a.IntVal != nil: + str := strconv.FormatInt(*a.IntVal, 10) + if a.Unit != "" { + str += " " + a.Unit + } + return str + case a.StringVal != nil: + return *a.StringVal + case a.BoolVal != nil: + return strconv.FormatBool(*a.BoolVal) + default: + return "" + } +} + // NodeDeviceLocality stores information about the devices hardware locality on // the node. type NodeDeviceLocality struct { diff --git a/command/helper_devices.go b/command/helper_devices.go index 6bdce2d97..ba88cb3fd 100644 --- a/command/helper_devices.go +++ b/command/helper_devices.go @@ -109,3 +109,15 @@ func printDeviceStats(ui cli.Ui, deviceGroupStats []*api.DeviceGroupStats) { } } } + +func getDeviceAttributes(d *api.NodeDeviceResource) []string { + attrs := []string{fmt.Sprintf("Device Group|%s", d.ID())} + + for k, v := range d.Attributes { + attrs = append(attrs, k+"|"+v.String()) + } + + sort.Strings(attrs[1:]) + + return attrs +} diff --git a/command/helper_devices_test.go b/command/helper_devices_test.go index 5c71582fa..028d72701 100644 --- a/command/helper_devices_test.go +++ b/command/helper_devices_test.go @@ -247,3 +247,29 @@ func TestNodeStatusCommand_GetDeviceResources(t *testing.T) { assert.Equal(t, expected, formattedDevices) } +func TestGetDeviceAttributes(t *testing.T) { + d := &api.NodeDeviceResource{ + Vendor: "Vendor", + Type: "Type", + Name: "Name", + + Attributes: map[string]*api.Attribute{ + "utilization": &api.Attribute{ + FloatVal: helper.Float64ToPtr(0.78), + Unit: "%", + }, + "filesystem": &api.Attribute{ + StringVal: helper.StringToPtr("ext4"), + }, + }, + } + + formattedDevices := getDeviceAttributes(d) + expected := []string{ + "Device Group|Vendor/Type/Name", + "filesystem|ext4", + "utilization|0.78 %", + } + + assert.Equal(t, expected, formattedDevices) +} diff --git a/command/node_status.go b/command/node_status.go index f722feda1..0785181a7 100644 --- a/command/node_status.go +++ b/command/node_status.go @@ -419,6 +419,7 @@ func (c *NodeStatusCommand) formatNode(client *api.Client, node *api.Node) int { if c.verbose { c.formatAttributes(node) + c.formatDeviceAttributes(node) c.formatMeta(node) } return 0 @@ -529,6 +530,32 @@ func (c *NodeStatusCommand) formatAttributes(node *api.Node) { c.Ui.Output(formatKV(attributes)) } +func (c *NodeStatusCommand) formatDeviceAttributes(node *api.Node) { + devices := node.NodeResources.Devices + if len(devices) == 0 { + return + } + + sort.Slice(devices, func(i, j int) bool { + return devices[i].ID() < devices[j].ID() + }) + + first := true + for _, d := range devices { + if len(d.Attributes) == 0 { + continue + } + + if first { + c.Ui.Output("\nDevice Group Attributes") + first = false + } else { + c.Ui.Output("") + } + c.Ui.Output(formatKV(getDeviceAttributes(d))) + } +} + func (c *NodeStatusCommand) formatMeta(node *api.Node) { // Print the meta keys := make([]string, 0, len(node.Meta)) From 40164b3dc68cffec818473dd03531c2b14fa866c Mon Sep 17 00:00:00 2001 From: Mahmood Ali Date: Mon, 10 Dec 2018 12:17:00 -0500 Subject: [PATCH 4/5] Use max 3 precision in displaying floats When formating floats in `nomad node status`, use a maximum precision of 3. --- api/nodes.go | 5 +++-- api/resources.go | 2 +- helper/funcs.go | 25 +++++++++++++++++++++++++ helper/funcs_test.go | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 3 deletions(-) diff --git a/api/nodes.go b/api/nodes.go index bfed5d8eb..bf7709fea 100644 --- a/api/nodes.go +++ b/api/nodes.go @@ -7,6 +7,7 @@ import ( "strconv" "time" + "github.com/hashicorp/nomad/helper" "github.com/hashicorp/nomad/nomad/structs" ) @@ -673,9 +674,9 @@ func (v *StatValue) String() string { case v.StringVal != nil: return *v.StringVal case v.FloatNumeratorVal != nil: - str := strconv.FormatFloat(*v.FloatNumeratorVal, 'f', -1, 64) + str := helper.FormatFloat(*v.FloatNumeratorVal, 3) if v.FloatDenominatorVal != nil { - str += " / " + strconv.FormatFloat(*v.FloatDenominatorVal, 'f', -1, 64) + str += " / " + helper.FormatFloat(*v.FloatDenominatorVal, 3) } if v.Unit != "" { diff --git a/api/resources.go b/api/resources.go index 767d82625..d2a1af965 100644 --- a/api/resources.go +++ b/api/resources.go @@ -172,7 +172,7 @@ type Attribute struct { func (a Attribute) String() string { switch { case a.FloatVal != nil: - str := strconv.FormatFloat(*a.FloatVal, 'f', -1, 64) + str := helper.FormatFloat(*a.FloatVal, 3) if a.Unit != "" { str += " " + a.Unit } diff --git a/helper/funcs.go b/helper/funcs.go index 89027b4cb..cf9cc3dae 100644 --- a/helper/funcs.go +++ b/helper/funcs.go @@ -4,6 +4,8 @@ import ( "crypto/sha512" "fmt" "regexp" + "strconv" + "strings" "time" multierror "github.com/hashicorp/go-multierror" @@ -344,3 +346,26 @@ func CheckHCLKeys(node ast.Node, valid []string) error { return result } + +// FormatFloat converts the floating-point number f to a string, +// after rounding it to the passed unit. +// +// Uses 'f' format (-ddd.dddddd, no exponent), and uses at most +// maxPrec digits after the decimal point. +func FormatFloat(f float64, maxPrec int) string { + v := strconv.FormatFloat(f, 'f', -1, 64) + + idx := strings.LastIndex(v, ".") + if idx == -1 { + return v + } + + sublen := idx + maxPrec + 1 + if sublen > len(v) { + sublen = len(v) + } + + return v[:sublen] + + return v +} diff --git a/helper/funcs_test.go b/helper/funcs_test.go index 774030be1..e9c9cdcd7 100644 --- a/helper/funcs_test.go +++ b/helper/funcs_test.go @@ -4,6 +4,8 @@ import ( "reflect" "sort" "testing" + + "github.com/stretchr/testify/require" ) func TestSliceStringIsSubset(t *testing.T) { @@ -87,3 +89,35 @@ func BenchmarkCleanEnvVar(b *testing.B) { CleanEnvVar(in, replacement) } } + +func TestFormatRoundedFloat(t *testing.T) { + cases := []struct { + input float64 + expected string + }{ + { + 1323, + "1323", + }, + { + 10.321, + "10.321", + }, + { + 100000.31324324, + "100000.313", + }, + { + 100000.3, + "100000.3", + }, + { + 0.7654321, + "0.765", + }, + } + + for _, c := range cases { + require.Equal(t, c.expected, FormatFloat(c.input, 3)) + } +} From bc6929b8fdf6feed62fc975d132e43121ffacc5f Mon Sep 17 00:00:00 2001 From: Mahmood Ali Date: Wed, 12 Dec 2018 09:17:31 -0500 Subject: [PATCH 5/5] fixup! device attributes in `nomad node status -verbose` --- command/helper_devices_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/helper_devices_test.go b/command/helper_devices_test.go index 028d72701..a54af1cad 100644 --- a/command/helper_devices_test.go +++ b/command/helper_devices_test.go @@ -254,11 +254,11 @@ func TestGetDeviceAttributes(t *testing.T) { Name: "Name", Attributes: map[string]*api.Attribute{ - "utilization": &api.Attribute{ + "utilization": { FloatVal: helper.Float64ToPtr(0.78), Unit: "%", }, - "filesystem": &api.Attribute{ + "filesystem": { StringVal: helper.StringToPtr("ext4"), }, },