1
0
Fork 0
mirror of https://github.com/kubernetes-sigs/node-feature-discovery.git synced 2025-03-15 21:08:23 +00:00

Support for non-binary labels

Make it possible to advertise also other than simple 'true' values for
feature labels.
This commit is contained in:
Markus Lehtonen 2018-06-21 19:02:30 +03:00
parent b0fedab786
commit 56c2ab3d58
16 changed files with 99 additions and 54 deletions

View file

@ -96,6 +96,7 @@ The published node labels encode a few pieces of information:
- The source for each label (e.g. `cpuid`). - The source for each label (e.g. `cpuid`).
- The name of the discovered feature as it appears in the underlying - The name of the discovered feature as it appears in the underlying
source, (e.g. `AESNI` from cpuid). source, (e.g. `AESNI` from cpuid).
- The value of the discovered feature.
Feature label names adhere to the following pattern: Feature label names adhere to the following pattern:
``` ```

View file

@ -332,8 +332,8 @@ func getFeatureLabels(source source.FeatureSource) (labels Labels, err error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, f := range features { for k := range features {
labels[fmt.Sprintf("%s-%s-%s", prefix, source.Name(), f)] = "true" labels[fmt.Sprintf("%s-%s-%s", prefix, source.Name(), k)] = fmt.Sprintf("%v", features[k])
} }
return labels, nil return labels, nil
} }

View file

@ -22,9 +22,11 @@ func TestDiscoveryWithMockSources(t *testing.T) {
Convey("When I discover features from fake source and update the node using fake client", t, func() { Convey("When I discover features from fake source and update the node using fake client", t, func() {
mockFeatureSource := new(MockFeatureSource) mockFeatureSource := new(MockFeatureSource)
fakeFeatureSourceName := string("testSource") fakeFeatureSourceName := string("testSource")
fakeFeatures := []string{"testfeature1", "testfeature2", "testfeature3"} fakeFeatureNames := []string{"testfeature1", "testfeature2", "testfeature3"}
fakeFeatures := source.Features{}
fakeFeatureLabels := Labels{} fakeFeatureLabels := Labels{}
for _, f := range fakeFeatures { for _, f := range fakeFeatureNames {
fakeFeatures[f] = true
fakeFeatureLabels[fmt.Sprintf("%s-testSource-%s", prefix, f)] = "true" fakeFeatureLabels[fmt.Sprintf("%s-testSource-%s", prefix, f)] = "true"
} }
fakeFeatureSource := source.FeatureSource(mockFeatureSource) fakeFeatureSource := source.FeatureSource(mockFeatureSource)

View file

@ -1,6 +1,9 @@
package main package main
import "github.com/stretchr/testify/mock" import (
"github.com/kubernetes-incubator/node-feature-discovery/source"
"github.com/stretchr/testify/mock"
)
type MockFeatureSource struct { type MockFeatureSource struct {
mock.Mock mock.Mock
@ -23,15 +26,15 @@ func (_m *MockFeatureSource) Name() string {
// Discover provides a mock function with no input arguments // Discover provides a mock function with no input arguments
// and []string and error as the return values // and []string and error as the return values
func (_m *MockFeatureSource) Discover() ([]string, error) { func (_m *MockFeatureSource) Discover() (source.Features, error) {
ret := _m.Called() ret := _m.Called()
var r0 []string var r0 source.Features
if rf, ok := ret.Get(0).(func() []string); ok { if rf, ok := ret.Get(0).(func() source.Features); ok {
r0 = rf() r0 = rf()
} else { } else {
if ret.Get(0) != nil { if ret.Get(0) != nil {
r0 = ret.Get(0).([]string) r0 = ret.Get(0).(source.Features)
} }
} }

View file

@ -16,7 +16,10 @@ limitations under the License.
package cpuid package cpuid
import "github.com/klauspost/cpuid" import (
"github.com/klauspost/cpuid"
"github.com/kubernetes-incubator/node-feature-discovery/source"
)
// Source implements FeatureSource. // Source implements FeatureSource.
type Source struct{} type Source struct{}
@ -25,7 +28,11 @@ type Source struct{}
func (s Source) Name() string { return "cpuid" } func (s Source) Name() string { return "cpuid" }
// Discover returns feature names for all the supported CPU features. // Discover returns feature names for all the supported CPU features.
func (s Source) Discover() ([]string, error) { func (s Source) Discover() (source.Features, error) {
// Get the cpu features as strings // Get the cpu features as strings
return cpuid.CPU.Features.Strings(), nil features := source.Features{}
for _, f := range cpuid.CPU.Features.Strings() {
features[f] = true
}
return features, nil
} }

View file

@ -16,6 +16,8 @@ limitations under the License.
package fake package fake
import "github.com/kubernetes-incubator/node-feature-discovery/source"
// Source implements FeatureSource. // Source implements FeatureSource.
type Source struct{} type Source struct{}
@ -23,11 +25,13 @@ type Source struct{}
func (s Source) Name() string { return "fake" } func (s Source) Name() string { return "fake" }
// Discover returns feature names for some fake features. // Discover returns feature names for some fake features.
func (s Source) Discover() ([]string, error) { func (s Source) Discover() (source.Features, error) {
features := []string{}
// Adding three fake features. // Adding three fake features.
features = append(features, "fakefeature1", "fakefeature2", "fakefeature3") features := source.Features{
"fakefeature1": true,
"fakefeature2": true,
"fakefeature3": true,
}
return features, nil return features, nil
} }

View file

@ -19,6 +19,8 @@ package iommu
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"github.com/kubernetes-incubator/node-feature-discovery/source"
) )
// Implement FeatureSource interface // Implement FeatureSource interface
@ -26,8 +28,8 @@ type Source struct{}
func (s Source) Name() string { return "iommu" } func (s Source) Name() string { return "iommu" }
func (s Source) Discover() ([]string, error) { func (s Source) Discover() (source.Features, error) {
features := []string{} features := source.Features{}
// Check if any iommu devices are available // Check if any iommu devices are available
devices, err := ioutil.ReadDir("/sys/class/iommu/") devices, err := ioutil.ReadDir("/sys/class/iommu/")
@ -36,7 +38,7 @@ func (s Source) Discover() ([]string, error) {
} }
if len(devices) > 0 { if len(devices) > 0 {
features = append(features, "enabled") features["enabled"] = true
} }
return features, nil return features, nil

View file

@ -20,6 +20,8 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"strings" "strings"
"github.com/kubernetes-incubator/node-feature-discovery/source"
) )
// Source implements FeatureSource. // Source implements FeatureSource.
@ -29,8 +31,8 @@ type Source struct{}
func (s Source) Name() string { return "memory" } func (s Source) Name() string { return "memory" }
// Discover returns feature names for memory: numa if more than one memory node is present. // Discover returns feature names for memory: numa if more than one memory node is present.
func (s Source) Discover() ([]string, error) { func (s Source) Discover() (source.Features, error) {
features := []string{} features := source.Features{}
// Find out how many nodes are online // Find out how many nodes are online
// Multiple nodes is a sign of NUMA // Multiple nodes is a sign of NUMA
@ -44,7 +46,7 @@ func (s Source) Discover() ([]string, error) {
// presence of newline requires TrimSpace // presence of newline requires TrimSpace
if strings.TrimSpace(string(bytes)) != "0" { if strings.TrimSpace(string(bytes)) != "0" {
// more than one node means NUMA // more than one node means NUMA
features = append(features, "numa") features["numa"] = true
} }
return features, nil return features, nil

View file

@ -24,6 +24,8 @@ import (
"net" "net"
"strconv" "strconv"
"strings" "strings"
"github.com/kubernetes-incubator/node-feature-discovery/source"
) )
// Source implements FeatureSource. // Source implements FeatureSource.
@ -33,8 +35,8 @@ type Source struct{}
func (s Source) Name() string { return "network" } func (s Source) Name() string { return "network" }
// Discover returns feature names sriov-configured and sriov if SR-IOV capable NICs are present and/or SR-IOV virtual functions are configured on the node // Discover returns feature names sriov-configured and sriov if SR-IOV capable NICs are present and/or SR-IOV virtual functions are configured on the node
func (s Source) Discover() ([]string, error) { func (s Source) Discover() (source.Features, error) {
features := []string{} features := source.Features{}
netInterfaces, err := net.Interfaces() netInterfaces, err := net.Interfaces()
if err != nil { if err != nil {
return nil, fmt.Errorf("can't obtain the network interfaces details: %s", err.Error()) return nil, fmt.Errorf("can't obtain the network interfaces details: %s", err.Error())
@ -57,7 +59,7 @@ func (s Source) Discover() ([]string, error) {
if t > 0 { if t > 0 {
glog.Infof("SR-IOV capability is detected on the network interface: %s", netInterface.Name) glog.Infof("SR-IOV capability is detected on the network interface: %s", netInterface.Name)
glog.Infof("%d maximum supported number of virtual functions on network interface: %s", t, netInterface.Name) glog.Infof("%d maximum supported number of virtual functions on network interface: %s", t, netInterface.Name)
features = append(features, "sriov.capable") features["sriov.capable"] = true
numVfsPath := "/sys/class/net/" + netInterface.Name + "/device/sriov_numvfs" numVfsPath := "/sys/class/net/" + netInterface.Name + "/device/sriov_numvfs"
numBytes, err := ioutil.ReadFile(numVfsPath) numBytes, err := ioutil.ReadFile(numVfsPath)
if err != nil { if err != nil {
@ -72,7 +74,7 @@ func (s Source) Discover() ([]string, error) {
} }
if n > 0 { if n > 0 {
glog.Infof("%d virtual functions configured on network interface: %s", n, netInterface.Name) glog.Infof("%d virtual functions configured on network interface: %s", n, netInterface.Name)
features = append(features, "sriov.configured") features["sriov.configured"] = true
break break
} else if n == 0 { } else if n == 0 {
glog.Errorf("SR-IOV not configured on network interface: %s", netInterface.Name) glog.Errorf("SR-IOV not configured on network interface: %s", netInterface.Name)

View file

@ -16,6 +16,8 @@ limitations under the License.
package panic_fake package panic_fake
import "github.com/kubernetes-incubator/node-feature-discovery/source"
// Source implements FeatureSource. // Source implements FeatureSource.
type Source struct{} type Source struct{}
@ -23,6 +25,6 @@ type Source struct{}
func (s Source) Name() string { return "panic_fake" } func (s Source) Name() string { return "panic_fake" }
// Discover calls panic(). // Discover calls panic().
func (s Source) Discover() ([]string, error) { func (s Source) Discover() (source.Features, error) {
panic("fake panic error") panic("fake panic error")
} }

View file

@ -23,6 +23,8 @@ import (
"os" "os"
"path" "path"
"strings" "strings"
"github.com/kubernetes-incubator/node-feature-discovery/source"
) )
type pciDeviceInfo map[string]string type pciDeviceInfo map[string]string
@ -48,8 +50,8 @@ type Source struct{}
func (s Source) Name() string { return "pci" } func (s Source) Name() string { return "pci" }
// Discover features // Discover features
func (s Source) Discover() ([]string, error) { func (s Source) Discover() (source.Features, error) {
features := map[string]bool{} features := source.Features{}
devs, err := detectPci() devs, err := detectPci()
if err != nil { if err != nil {
@ -100,12 +102,7 @@ func (s Source) Discover() ([]string, error) {
} }
} }
feature_list := []string{} return features, nil
for feature := range features {
feature_list = append(feature_list, feature)
}
return feature_list, nil
} }
// Read information of one PCI device // Read information of one PCI device

View file

@ -20,6 +20,8 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"runtime" "runtime"
"github.com/kubernetes-incubator/node-feature-discovery/source"
) )
// Source implements FeatureSource. // Source implements FeatureSource.
@ -29,8 +31,8 @@ type Source struct{}
func (s Source) Name() string { return "pstate" } func (s Source) Name() string { return "pstate" }
// Discover returns feature names for p-state related features such as turbo boost. // Discover returns feature names for p-state related features such as turbo boost.
func (s Source) Discover() ([]string, error) { func (s Source) Discover() (source.Features, error) {
features := []string{} features := source.Features{}
// On Arm platform, the frequency boost mechanism is software-based. // On Arm platform, the frequency boost mechanism is software-based.
// So skip pstate detection on Arm. // So skip pstate detection on Arm.
@ -46,7 +48,7 @@ func (s Source) Discover() ([]string, error) {
} }
if bytes[0] == byte('0') { if bytes[0] == byte('0') {
// Turbo boost is enabled. // Turbo boost is enabled.
features = append(features, "turbo") features["turbo"] = true
} }
return features, nil return features, nil

View file

@ -20,6 +20,7 @@ import (
"os/exec" "os/exec"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/kubernetes-incubator/node-feature-discovery/source"
) )
// Source implements FeatureSource. // Source implements FeatureSource.
@ -29,15 +30,15 @@ type Source struct{}
func (s Source) Name() string { return "rdt" } func (s Source) Name() string { return "rdt" }
// Discover returns feature names for CMT and CAT if supported. // Discover returns feature names for CMT and CAT if supported.
func (s Source) Discover() ([]string, error) { func (s Source) Discover() (source.Features, error) {
features := []string{} features := source.Features{}
cmd := exec.Command("bash", "-c", "mon-discovery") cmd := exec.Command("bash", "-c", "mon-discovery")
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
glog.Errorf("support for RDT monitoring was not detected: %v", err) glog.Errorf("support for RDT monitoring was not detected: %v", err)
} else { } else {
// RDT monitoring detected. // RDT monitoring detected.
features = append(features, "RDTMON") features["RDTMON"] = true
} }
cmd = exec.Command("bash", "-c", "mon-cmt-discovery") cmd = exec.Command("bash", "-c", "mon-cmt-discovery")
@ -45,7 +46,7 @@ func (s Source) Discover() ([]string, error) {
glog.Errorf("support for RDT CMT monitoring was not detected: %v", err) glog.Errorf("support for RDT CMT monitoring was not detected: %v", err)
} else { } else {
// RDT CMT monitoring detected. // RDT CMT monitoring detected.
features = append(features, "RDTCMT") features["RDTCMT"] = true
} }
cmd = exec.Command("bash", "-c", "mon-mbm-discovery") cmd = exec.Command("bash", "-c", "mon-mbm-discovery")
@ -53,7 +54,7 @@ func (s Source) Discover() ([]string, error) {
glog.Errorf("support for RDT MBM monitoring was not detected: %v", err) glog.Errorf("support for RDT MBM monitoring was not detected: %v", err)
} else { } else {
// RDT MBM monitoring detected. // RDT MBM monitoring detected.
features = append(features, "RDTMBM") features["RDTMBM"] = true
} }
cmd = exec.Command("bash", "-c", "l3-alloc-discovery") cmd = exec.Command("bash", "-c", "l3-alloc-discovery")
@ -61,7 +62,7 @@ func (s Source) Discover() ([]string, error) {
glog.Errorf("support for RDT L3 allocation was not detected: %v", err) glog.Errorf("support for RDT L3 allocation was not detected: %v", err)
} else { } else {
// RDT L3 cache allocation detected. // RDT L3 cache allocation detected.
features = append(features, "RDTL3CA") features["RDTL3CA"] = true
} }
cmd = exec.Command("bash", "-c", "l2-alloc-discovery") cmd = exec.Command("bash", "-c", "l2-alloc-discovery")
@ -69,7 +70,7 @@ func (s Source) Discover() ([]string, error) {
glog.Errorf("support for RDT L2 allocation was not detected: %v", err) glog.Errorf("support for RDT L2 allocation was not detected: %v", err)
} else { } else {
// RDT L2 cache allocation detected. // RDT L2 cache allocation detected.
features = append(features, "RDTL2CA") features["RDTL2CA"] = true
} }
cmd = exec.Command("bash", "-c", "mem-bandwidth-alloc-discovery") cmd = exec.Command("bash", "-c", "mem-bandwidth-alloc-discovery")
@ -77,7 +78,7 @@ func (s Source) Discover() ([]string, error) {
glog.Errorf("support for RDT Memory bandwidth allocation was not detected: %v", err) glog.Errorf("support for RDT Memory bandwidth allocation was not detected: %v", err)
} else { } else {
// RDT Memory bandwidth allocation detected. // RDT Memory bandwidth allocation detected.
features = append(features, "RDTMBA") features["RDTMBA"] = true
} }
return features, nil return features, nil

View file

@ -17,23 +17,25 @@ limitations under the License.
package selinux package selinux
import ( import (
"io/ioutil"
"fmt" "fmt"
"io/ioutil"
"github.com/kubernetes-incubator/node-feature-discovery/source"
) )
type Source struct{} type Source struct{}
func (s Source) Name() string { return "selinux" } func (s Source) Name() string { return "selinux" }
func (s Source) Discover() ([]string, error) { func (s Source) Discover() (source.Features, error) {
features := []string{} features := source.Features{}
status, err := ioutil.ReadFile("/host-sys/fs/selinux/enforce") status, err := ioutil.ReadFile("/host-sys/fs/selinux/enforce")
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to detect the status of selinux, please check if the system supports selinux and make sure /sys on the host is mounted into the container: %s", err.Error()) return nil, fmt.Errorf("Failed to detect the status of selinux, please check if the system supports selinux and make sure /sys on the host is mounted into the container: %s", err.Error())
} }
if status[0] == byte('1') { if status[0] == byte('1') {
// selinux is enabled. // selinux is enabled.
features = append(features, "enabled") features["enabled"] = true
} }
return features, nil return features, nil
} }

View file

@ -16,11 +16,27 @@ limitations under the License.
package source package source
// Value of a feature
type FeatureValue interface {
}
// Boolean feature value
type BoolFeatureValue bool
func (b BoolFeatureValue) String() string {
if b == true {
return "true"
}
return "false"
}
type Features map[string]FeatureValue
// FeatureSource represents a source of a discovered node feature. // FeatureSource represents a source of a discovered node feature.
type FeatureSource interface { type FeatureSource interface {
// Name returns a friendly name for this source of node feature. // Name returns a friendly name for this source of node feature.
Name() string Name() string
// Discover returns discovered features for this node. // Discover returns discovered features for this node.
Discover() ([]string, error) Discover() (Features, error)
} }

View file

@ -19,6 +19,8 @@ package storage
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"github.com/kubernetes-incubator/node-feature-discovery/source"
) )
// Source implements FeatureSource. // Source implements FeatureSource.
@ -28,8 +30,8 @@ type Source struct{}
func (s Source) Name() string { return "storage" } func (s Source) Name() string { return "storage" }
// Discover returns feature names for storage: nonrotationaldisk if any SSD drive present. // Discover returns feature names for storage: nonrotationaldisk if any SSD drive present.
func (s Source) Discover() ([]string, error) { func (s Source) Discover() (source.Features, error) {
features := []string{} features := source.Features{}
// Check if there is any non-rotational block devices attached to the node // Check if there is any non-rotational block devices attached to the node
blockdevices, err := ioutil.ReadDir("/sys/block/") blockdevices, err := ioutil.ReadDir("/sys/block/")
@ -42,7 +44,7 @@ func (s Source) Discover() ([]string, error) {
} }
if bytes[0] == byte('0') { if bytes[0] == byte('0') {
// Non-rotational storage is present, add label. // Non-rotational storage is present, add label.
features = append(features, "nonrotationaldisk") features["nonrotationaldisk"] = true
break break
} }
} }