mirror of
https://github.com/kubernetes-sigs/node-feature-discovery.git
synced 2025-03-28 02:37:11 +00:00
Merge pull request #304 from marquiz/devel/config-reload
Rework config handling
This commit is contained in:
commit
e9017bef06
20 changed files with 518 additions and 236 deletions
22
README.md
22
README.md
|
@ -714,8 +714,11 @@ kubectl create -f nfd-worker-daemonset.yaml
|
|||
Nfd-worker connects to the nfd-master service to advertise hardware features.
|
||||
|
||||
When run as a daemonset, nodes are re-labeled at an interval specified using
|
||||
the `--sleep-interval` option. In the [template](https://github.com/kubernetes-sigs/node-feature-discovery/blob/master/nfd-worker-daemonset.yaml.template#L26) the default interval is set to 60s
|
||||
which is also the default when no `--sleep-interval` is specified.
|
||||
the `--sleep-interval` option. In the
|
||||
[template](https://github.com/kubernetes-sigs/node-feature-discovery/blob/master/nfd-worker-daemonset.yaml.template#L26)
|
||||
the default interval is set to 60s which is also the default when no
|
||||
`--sleep-interval` is specified. Also, the configuration file is re-read on
|
||||
each iteration providing a simple mechanism of run-time reconfiguration.
|
||||
|
||||
Feature discovery can alternatively be configured as a one-shot job. There is
|
||||
an example script in this repo that demonstrates how to deploy the job in the cluster.
|
||||
|
@ -771,11 +774,16 @@ each nfd-worker requires a individual node-specific TLS certificate.
|
|||
|
||||
Nfd-worker supports a configuration file. The default location is
|
||||
`/etc/kubernetes/node-feature-discovery/nfd-worker.conf`, but,
|
||||
this can be changed by specifying the`--config` command line flag. The file is
|
||||
read inside the container, and thus, Volumes and VolumeMounts are needed to
|
||||
make your configuration available for NFD. The preferred method is to use a
|
||||
ConfigMap.
|
||||
For example, create a config map using the example config as a template:
|
||||
this can be changed by specifying the`--config` command line flag.
|
||||
Configuration file is re-read on each labeling pass (determined by
|
||||
`--sleep-interval`) which makes run-time re-configuration of nfd-worker
|
||||
possible.
|
||||
|
||||
Worker configuration file is read inside the container, and thus, Volumes and
|
||||
VolumeMounts are needed to make your configuration available for NFD. The
|
||||
preferred method is to use a ConfigMap which provides easy deployment and
|
||||
re-configurability. For example, create a config map using the example config
|
||||
as a template:
|
||||
```
|
||||
cp nfd-worker.conf.example nfd-worker.conf
|
||||
vim nfd-worker.conf # edit the configuration
|
||||
|
|
|
@ -4,9 +4,12 @@
|
|||
|
||||
package apihelper
|
||||
|
||||
import kubernetes "k8s.io/client-go/kubernetes"
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
import v1 "k8s.io/api/core/v1"
|
||||
import (
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
kubernetes "k8s.io/client-go/kubernetes"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// MockAPIHelpers is an autogenerated mock type for the APIHelpers type
|
||||
type MockAPIHelpers struct {
|
||||
|
|
|
@ -4,9 +4,13 @@
|
|||
|
||||
package labeler
|
||||
|
||||
import context "context"
|
||||
import grpc "google.golang.org/grpc"
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
import (
|
||||
context "context"
|
||||
|
||||
grpc "google.golang.org/grpc"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// MockLabelerClient is an autogenerated mock type for the LabelerClient type
|
||||
type MockLabelerClient struct {
|
||||
|
|
|
@ -29,8 +29,11 @@ import (
|
|||
"github.com/vektra/errors"
|
||||
"sigs.k8s.io/node-feature-discovery/pkg/labeler"
|
||||
"sigs.k8s.io/node-feature-discovery/source"
|
||||
"sigs.k8s.io/node-feature-discovery/source/cpu"
|
||||
"sigs.k8s.io/node-feature-discovery/source/fake"
|
||||
"sigs.k8s.io/node-feature-discovery/source/kernel"
|
||||
"sigs.k8s.io/node-feature-discovery/source/panic_fake"
|
||||
"sigs.k8s.io/node-feature-discovery/source/pci"
|
||||
)
|
||||
|
||||
const fakeFeatureSourceName string = "testSource"
|
||||
|
@ -91,13 +94,27 @@ func makeFakeFeatures(names []string) (source.Features, Labels) {
|
|||
return features, labels
|
||||
}
|
||||
|
||||
func TestConfigParse(t *testing.T) {
|
||||
Convey("When parsing configuration file", t, func() {
|
||||
Convey("When non-accessible file is given", func() {
|
||||
err := configParse("non-existing-file", "")
|
||||
func (w *nfdWorker) getSource(name string) source.FeatureSource {
|
||||
for _, s := range w.sources {
|
||||
if s.Name() == name {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Convey("Should return error", func() {
|
||||
So(err, ShouldNotBeNil)
|
||||
func TestConfigParse(t *testing.T) {
|
||||
Convey("When parsing configuration", t, func() {
|
||||
w, err := NewNfdWorker(Args{Sources: []string{"cpu", "kernel", "pci"}})
|
||||
So(err, ShouldBeNil)
|
||||
worker := w.(*nfdWorker)
|
||||
Convey("and a non-accessible file and some overrides are specified", func() {
|
||||
overrides := `{"sources": {"cpu": {"cpuid": {"attributeBlacklist": ["foo","bar"]}}}}`
|
||||
worker.configure("non-existing-file", overrides)
|
||||
|
||||
Convey("overrides should take effect", func() {
|
||||
c := worker.getSource("cpu").GetConfig().(*cpu.Config)
|
||||
So(c.Cpuid.AttributeBlacklist, ShouldResemble, []string{"foo", "bar"})
|
||||
})
|
||||
})
|
||||
// Create a temporary config file
|
||||
|
@ -114,76 +131,87 @@ func TestConfigParse(t *testing.T) {
|
|||
f.Close()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
Convey("When proper config file is given", func() {
|
||||
err := configParse(f.Name(), "")
|
||||
Convey("and a proper config file is specified", func() {
|
||||
worker.configure(f.Name(), "")
|
||||
|
||||
Convey("Should return error", func() {
|
||||
Convey("specified configuration should take effect", func() {
|
||||
So(err, ShouldBeNil)
|
||||
So(config.Sources.Kernel.ConfigOpts, ShouldResemble, []string{"DMI"})
|
||||
So(config.Sources.Pci.DeviceClassWhitelist, ShouldResemble, []string{"ff"})
|
||||
c := worker.getSource("kernel").GetConfig()
|
||||
So(c.(*kernel.Config).ConfigOpts, ShouldResemble, []string{"DMI"})
|
||||
c = worker.getSource("pci").GetConfig()
|
||||
So(c.(*pci.Config).DeviceClassWhitelist, ShouldResemble, []string{"ff"})
|
||||
})
|
||||
})
|
||||
|
||||
Convey("and a proper config file and overrides are given", func() {
|
||||
overrides := `{"sources": {"pci": {"deviceClassWhitelist": ["03"]}}}`
|
||||
worker.configure(f.Name(), overrides)
|
||||
|
||||
Convey("overrides should take precedence over the config file", func() {
|
||||
So(err, ShouldBeNil)
|
||||
c := worker.getSource("kernel").GetConfig()
|
||||
So(c.(*kernel.Config).ConfigOpts, ShouldResemble, []string{"DMI"})
|
||||
c = worker.getSource("pci").GetConfig()
|
||||
So(c.(*pci.Config).DeviceClassWhitelist, ShouldResemble, []string{"03"})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestConfigureParameters(t *testing.T) {
|
||||
Convey("When configuring parameters for node feature discovery", t, func() {
|
||||
func TestNewNfdWorker(t *testing.T) {
|
||||
Convey("When creating new NfdWorker instance", t, func() {
|
||||
|
||||
Convey("When no sourcesWhiteList and labelWhiteListStr are passed", func() {
|
||||
sourcesWhiteList := []string{}
|
||||
labelWhiteListStr := ""
|
||||
Convey("without any args specified", func() {
|
||||
args := Args{}
|
||||
emptyRegexp, _ := regexp.Compile("")
|
||||
enabledSources, labelWhiteList, err := configureParameters(sourcesWhiteList, labelWhiteListStr)
|
||||
|
||||
Convey("Error should not be produced", func() {
|
||||
w, err := NewNfdWorker(args)
|
||||
Convey("no error should be returned", func() {
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
Convey("No sourcesWhiteList or labelWhiteList are returned", func() {
|
||||
So(len(enabledSources), ShouldEqual, 0)
|
||||
So(labelWhiteList, ShouldResemble, emptyRegexp)
|
||||
worker := w.(*nfdWorker)
|
||||
Convey("no sources should be enabled and the whitelist regexp should be empty", func() {
|
||||
So(len(worker.sources), ShouldEqual, 0)
|
||||
So(worker.labelWhiteList, ShouldResemble, emptyRegexp)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When sourcesWhiteList is passed", func() {
|
||||
sourcesWhiteList := []string{"fake"}
|
||||
labelWhiteListStr := ""
|
||||
Convey("with non-empty Sources arg specified", func() {
|
||||
args := Args{Sources: []string{"fake"}}
|
||||
emptyRegexp, _ := regexp.Compile("")
|
||||
enabledSources, labelWhiteList, err := configureParameters(sourcesWhiteList, labelWhiteListStr)
|
||||
|
||||
Convey("Error should not be produced", func() {
|
||||
w, err := NewNfdWorker(args)
|
||||
Convey("no error should be returned", func() {
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
Convey("Proper sourcesWhiteList are returned", func() {
|
||||
So(len(enabledSources), ShouldEqual, 1)
|
||||
So(enabledSources[0], ShouldHaveSameTypeAs, fake.Source{})
|
||||
So(labelWhiteList, ShouldResemble, emptyRegexp)
|
||||
worker := w.(*nfdWorker)
|
||||
Convey("proper sources should be enabled", func() {
|
||||
So(len(worker.sources), ShouldEqual, 1)
|
||||
So(worker.sources[0], ShouldHaveSameTypeAs, &fake.Source{})
|
||||
So(worker.labelWhiteList, ShouldResemble, emptyRegexp)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When invalid labelWhiteListStr is passed", func() {
|
||||
sourcesWhiteList := []string{""}
|
||||
labelWhiteListStr := "*"
|
||||
enabledSources, labelWhiteList, err := configureParameters(sourcesWhiteList, labelWhiteListStr)
|
||||
|
||||
Convey("Error is produced", func() {
|
||||
So(enabledSources, ShouldBeNil)
|
||||
So(labelWhiteList, ShouldBeNil)
|
||||
Convey("with invalid LabelWhiteList arg specified", func() {
|
||||
args := Args{LabelWhiteList: "*"}
|
||||
w, err := NewNfdWorker(args)
|
||||
worker := w.(*nfdWorker)
|
||||
Convey("an error should be returned", func() {
|
||||
So(len(worker.sources), ShouldEqual, 0)
|
||||
So(worker.labelWhiteList, ShouldBeNil)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When valid labelWhiteListStr is passed", func() {
|
||||
sourcesWhiteList := []string{""}
|
||||
labelWhiteListStr := ".*rdt.*"
|
||||
expectRegexp, err := regexp.Compile(".*rdt.*")
|
||||
enabledSources, labelWhiteList, err := configureParameters(sourcesWhiteList, labelWhiteListStr)
|
||||
|
||||
Convey("Error should not be produced", func() {
|
||||
Convey("with valid LabelWhiteListStr arg specified", func() {
|
||||
args := Args{LabelWhiteList: ".*rdt.*"}
|
||||
w, err := NewNfdWorker(args)
|
||||
Convey("no error should be returned", func() {
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
Convey("Proper labelWhiteList is returned", func() {
|
||||
So(len(enabledSources), ShouldEqual, 0)
|
||||
So(labelWhiteList, ShouldResemble, expectRegexp)
|
||||
worker := w.(*nfdWorker)
|
||||
expectRegexp := regexp.MustCompile(".*rdt.*")
|
||||
Convey("proper labelWhiteList regexp should be produced", func() {
|
||||
So(len(worker.sources), ShouldEqual, 0)
|
||||
So(worker.labelWhiteList, ShouldResemble, expectRegexp)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -19,6 +19,7 @@ package nfdworker
|
|||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
|
@ -50,7 +51,6 @@ import (
|
|||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
// package loggers
|
||||
var (
|
||||
stdoutLogger = log.New(os.Stdout, "", log.LstdFlags)
|
||||
stderrLogger = log.New(os.Stderr, "", log.LstdFlags)
|
||||
|
@ -59,16 +59,10 @@ var (
|
|||
|
||||
// Global config
|
||||
type NFDConfig struct {
|
||||
Sources struct {
|
||||
CPU *cpu.NFDConfig `json:"cpu,omitempty"`
|
||||
Kernel *kernel.NFDConfig `json:"kernel,omitempty"`
|
||||
Pci *pci.NFDConfig `json:"pci,omitempty"`
|
||||
Usb *usb.NFDConfig `json:"usb,omitempty"`
|
||||
Custom *custom.NFDConfig `json:"custom,omitempty"`
|
||||
} `json:"sources,omitempty"`
|
||||
Sources sourcesConfig
|
||||
}
|
||||
|
||||
var config = NFDConfig{}
|
||||
type sourcesConfig map[string]source.Config
|
||||
|
||||
// Labels are a Kubernetes representation of discovered features.
|
||||
type Labels map[string]string
|
||||
|
@ -94,14 +88,21 @@ type NfdWorker interface {
|
|||
}
|
||||
|
||||
type nfdWorker struct {
|
||||
args Args
|
||||
clientConn *grpc.ClientConn
|
||||
client pb.LabelerClient
|
||||
args Args
|
||||
clientConn *grpc.ClientConn
|
||||
client pb.LabelerClient
|
||||
config NFDConfig
|
||||
sources []source.FeatureSource
|
||||
labelWhiteList *regexp.Regexp
|
||||
}
|
||||
|
||||
// Create new NfdWorker instance.
|
||||
func NewNfdWorker(args Args) (NfdWorker, error) {
|
||||
nfd := &nfdWorker{args: args}
|
||||
nfd := &nfdWorker{
|
||||
args: args,
|
||||
sources: []source.FeatureSource{},
|
||||
}
|
||||
|
||||
if args.SleepInterval > 0 && args.SleepInterval < time.Second {
|
||||
stderrLogger.Printf("WARNING: too short sleep-intervall specified (%s), forcing to 1s", args.SleepInterval.String())
|
||||
args.SleepInterval = time.Second
|
||||
|
@ -120,6 +121,44 @@ func NewNfdWorker(args Args) (NfdWorker, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Figure out active sources
|
||||
allSources := []source.FeatureSource{
|
||||
&cpu.Source{},
|
||||
&fake.Source{},
|
||||
&iommu.Source{},
|
||||
&kernel.Source{},
|
||||
&memory.Source{},
|
||||
&network.Source{},
|
||||
&panicfake.Source{},
|
||||
&pci.Source{},
|
||||
&storage.Source{},
|
||||
&system.Source{},
|
||||
&usb.Source{},
|
||||
&custom.Source{},
|
||||
// local needs to be the last source so that it is able to override
|
||||
// labels from other sources
|
||||
&local.Source{},
|
||||
}
|
||||
|
||||
sourceWhiteList := map[string]struct{}{}
|
||||
for _, s := range args.Sources {
|
||||
sourceWhiteList[strings.TrimSpace(s)] = struct{}{}
|
||||
}
|
||||
|
||||
nfd.sources = []source.FeatureSource{}
|
||||
for _, s := range allSources {
|
||||
if _, enabled := sourceWhiteList[s.Name()]; enabled {
|
||||
nfd.sources = append(nfd.sources, s)
|
||||
}
|
||||
}
|
||||
|
||||
// Compile labelWhiteList regex
|
||||
var err error
|
||||
nfd.labelWhiteList, err = regexp.Compile(args.LabelWhiteList)
|
||||
if err != nil {
|
||||
return nfd, fmt.Errorf("error parsing label whitelist regex (%s): %s", args.LabelWhiteList, err)
|
||||
}
|
||||
|
||||
return nfd, nil
|
||||
}
|
||||
|
||||
|
@ -129,28 +168,19 @@ func (w *nfdWorker) Run() error {
|
|||
stdoutLogger.Printf("Node Feature Discovery Worker %s", version.Get())
|
||||
stdoutLogger.Printf("NodeName: '%s'", nodeName)
|
||||
|
||||
// Parse config
|
||||
err := configParse(w.args.ConfigFile, w.args.Options)
|
||||
if err != nil {
|
||||
stderrLogger.Print(err)
|
||||
}
|
||||
|
||||
// Configure the parameters for feature discovery.
|
||||
enabledSources, labelWhiteList, err := configureParameters(w.args.Sources, w.args.LabelWhiteList)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error occurred while configuring parameters: %s", err.Error())
|
||||
}
|
||||
|
||||
// Connect to NFD master
|
||||
err = w.connect()
|
||||
err := w.connect()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
defer w.disconnect()
|
||||
|
||||
for {
|
||||
// Parse and apply configuration
|
||||
w.configure(w.args.ConfigFile, w.args.Options)
|
||||
|
||||
// Get the set of feature labels.
|
||||
labels := createFeatureLabels(enabledSources, labelWhiteList)
|
||||
labels := createFeatureLabels(w.sources, w.labelWhiteList)
|
||||
|
||||
// Update the node with the feature labels.
|
||||
if w.client != nil {
|
||||
|
@ -236,76 +266,38 @@ func (w *nfdWorker) disconnect() {
|
|||
}
|
||||
|
||||
// Parse configuration options
|
||||
func configParse(filepath string, overrides string) error {
|
||||
config.Sources.CPU = &cpu.Config
|
||||
config.Sources.Kernel = &kernel.Config
|
||||
config.Sources.Pci = &pci.Config
|
||||
config.Sources.Usb = &usb.Config
|
||||
config.Sources.Custom = &custom.Config
|
||||
func (w *nfdWorker) configure(filepath string, overrides string) {
|
||||
// Create a new default config
|
||||
c := NFDConfig{Sources: make(map[string]source.Config, len(w.sources))}
|
||||
for _, s := range w.sources {
|
||||
c.Sources[s.Name()] = s.NewConfig()
|
||||
}
|
||||
|
||||
// Try to read and parse config file
|
||||
data, err := ioutil.ReadFile(filepath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read config file: %s", err)
|
||||
}
|
||||
|
||||
// Read config file
|
||||
err = yaml.Unmarshal(data, &config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to parse config file: %s", err)
|
||||
}
|
||||
|
||||
// Parse config overrides
|
||||
err = yaml.Unmarshal([]byte(overrides), &config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to parse --options: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// configureParameters returns all the variables required to perform feature
|
||||
// discovery based on command line arguments.
|
||||
func configureParameters(sourcesWhiteList []string, labelWhiteListStr string) (enabledSources []source.FeatureSource, labelWhiteList *regexp.Regexp, err error) {
|
||||
// A map for lookup
|
||||
sourcesWhiteListMap := map[string]struct{}{}
|
||||
for _, s := range sourcesWhiteList {
|
||||
sourcesWhiteListMap[strings.TrimSpace(s)] = struct{}{}
|
||||
}
|
||||
|
||||
// Configure feature sources.
|
||||
allSources := []source.FeatureSource{
|
||||
cpu.Source{},
|
||||
fake.Source{},
|
||||
iommu.Source{},
|
||||
kernel.Source{},
|
||||
memory.Source{},
|
||||
network.Source{},
|
||||
panicfake.Source{},
|
||||
pci.Source{},
|
||||
storage.Source{},
|
||||
system.Source{},
|
||||
usb.Source{},
|
||||
custom.Source{},
|
||||
// local needs to be the last source so that it is able to override
|
||||
// labels from other sources
|
||||
local.Source{},
|
||||
}
|
||||
|
||||
enabledSources = []source.FeatureSource{}
|
||||
for _, s := range allSources {
|
||||
if _, enabled := sourcesWhiteListMap[s.Name()]; enabled {
|
||||
enabledSources = append(enabledSources, s)
|
||||
stderrLogger.Printf("Failed to read config file: %s", err)
|
||||
} else {
|
||||
err = yaml.Unmarshal(data, &c)
|
||||
if err != nil {
|
||||
stderrLogger.Printf("Failed to parse config file: %s", err)
|
||||
} else {
|
||||
stdoutLogger.Printf("Configuration successfully loaded from %q", filepath)
|
||||
}
|
||||
}
|
||||
|
||||
// Compile labelWhiteList regex
|
||||
labelWhiteList, err = regexp.Compile(labelWhiteListStr)
|
||||
// Parse config overrides
|
||||
err = yaml.Unmarshal([]byte(overrides), &c)
|
||||
if err != nil {
|
||||
stderrLogger.Printf("error parsing whitelist regex (%s): %s", labelWhiteListStr, err)
|
||||
return nil, nil, err
|
||||
stderrLogger.Printf("Failed to parse --options: %s", err)
|
||||
}
|
||||
|
||||
return enabledSources, labelWhiteList, nil
|
||||
w.config = c
|
||||
|
||||
// (Re-)configure all sources
|
||||
for _, s := range w.sources {
|
||||
s.SetConfig(c.Sources[s.Name()])
|
||||
}
|
||||
}
|
||||
|
||||
// createFeatureLabels returns the set of feature labels from the enabled
|
||||
|
@ -350,7 +342,7 @@ func getFeatureLabels(source source.FeatureSource, labelWhiteList *regexp.Regexp
|
|||
// Prefix for labels in the default namespace
|
||||
prefix := source.Name() + "-"
|
||||
switch source.(type) {
|
||||
case local.Source:
|
||||
case *local.Source:
|
||||
// Do not prefix labels from the hooks
|
||||
prefix = ""
|
||||
}
|
||||
|
@ -416,3 +408,27 @@ func advertiseFeatureLabels(client pb.LabelerClient, labels Labels) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the Unmarshaler interface from "encoding/json"
|
||||
func (c *sourcesConfig) UnmarshalJSON(data []byte) error {
|
||||
// First do a raw parse to get the per-source data
|
||||
raw := map[string]json.RawMessage{}
|
||||
err := yaml.Unmarshal(data, &raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Then parse each source-specific data structure
|
||||
// NOTE: we expect 'c' to be pre-populated with correct per-source data
|
||||
// types. Non-pre-populated keys are ignored.
|
||||
for k, rawv := range raw {
|
||||
if v, ok := (*c)[k]; ok {
|
||||
err := yaml.Unmarshal(rawv, &v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse %q source config: %v", k, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -29,42 +29,43 @@ type cpuidConfig struct {
|
|||
AttributeWhitelist []string `json:"attributeWhitelist,omitempty"`
|
||||
}
|
||||
|
||||
// NFDConfig is the type holding configuration of the cpuid feature source
|
||||
type NFDConfig struct {
|
||||
type Config struct {
|
||||
Cpuid cpuidConfig `json:"cpuid,omitempty"`
|
||||
}
|
||||
|
||||
// Config contains the configuration of the cpuid source
|
||||
var Config = NFDConfig{
|
||||
cpuidConfig{
|
||||
AttributeBlacklist: []string{
|
||||
"BMI1",
|
||||
"BMI2",
|
||||
"CLMUL",
|
||||
"CMOV",
|
||||
"CX16",
|
||||
"ERMS",
|
||||
"F16C",
|
||||
"HTT",
|
||||
"LZCNT",
|
||||
"MMX",
|
||||
"MMXEXT",
|
||||
"NX",
|
||||
"POPCNT",
|
||||
"RDRAND",
|
||||
"RDSEED",
|
||||
"RDTSCP",
|
||||
"SGX",
|
||||
"SGXLC",
|
||||
"SSE",
|
||||
"SSE2",
|
||||
"SSE3",
|
||||
"SSE4.1",
|
||||
"SSE4.2",
|
||||
"SSSE3",
|
||||
// newDefaultConfig returns a new config with pre-populated defaults
|
||||
func newDefaultConfig() *Config {
|
||||
return &Config{
|
||||
cpuidConfig{
|
||||
AttributeBlacklist: []string{
|
||||
"BMI1",
|
||||
"BMI2",
|
||||
"CLMUL",
|
||||
"CMOV",
|
||||
"CX16",
|
||||
"ERMS",
|
||||
"F16C",
|
||||
"HTT",
|
||||
"LZCNT",
|
||||
"MMX",
|
||||
"MMXEXT",
|
||||
"NX",
|
||||
"POPCNT",
|
||||
"RDRAND",
|
||||
"RDSEED",
|
||||
"RDTSCP",
|
||||
"SGX",
|
||||
"SGXLC",
|
||||
"SSE",
|
||||
"SSE2",
|
||||
"SSE3",
|
||||
"SSE4.1",
|
||||
"SSE4.2",
|
||||
"SSSE3",
|
||||
},
|
||||
AttributeWhitelist: []string{},
|
||||
},
|
||||
AttributeWhitelist: []string{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Filter for cpuid labels
|
||||
|
@ -73,19 +74,33 @@ type keyFilter struct {
|
|||
whitelist bool
|
||||
}
|
||||
|
||||
var cpuidFilter *keyFilter
|
||||
|
||||
// Implement FeatureSource interface
|
||||
type Source struct{}
|
||||
type Source struct {
|
||||
config *Config
|
||||
cpuidFilter *keyFilter
|
||||
}
|
||||
|
||||
func (s Source) Name() string { return "cpu" }
|
||||
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
features := source.Features{}
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return newDefaultConfig() }
|
||||
|
||||
if cpuidFilter == nil {
|
||||
initCpuidFilter()
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return s.config }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(conf source.Config) {
|
||||
switch v := conf.(type) {
|
||||
case *Config:
|
||||
s.config = v
|
||||
s.initCpuidFilter()
|
||||
default:
|
||||
log.Printf("PANIC: invalid config type: %T", conf)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Source) Discover() (source.Features, error) {
|
||||
features := source.Features{}
|
||||
|
||||
// Check if hyper-threading seems to be enabled
|
||||
found, err := haveThreadSiblings()
|
||||
|
@ -106,7 +121,7 @@ func (s Source) Discover() (source.Features, error) {
|
|||
// Detect CPUID
|
||||
cpuidFlags := getCpuidFlags()
|
||||
for _, f := range cpuidFlags {
|
||||
if cpuidFilter.unmask(f) {
|
||||
if s.cpuidFilter.unmask(f) {
|
||||
features["cpuid."+f] = true
|
||||
}
|
||||
}
|
||||
|
@ -155,20 +170,20 @@ func haveThreadSiblings() (bool, error) {
|
|||
return false, nil
|
||||
}
|
||||
|
||||
func initCpuidFilter() {
|
||||
func (s *Source) initCpuidFilter() {
|
||||
newFilter := keyFilter{keys: map[string]struct{}{}}
|
||||
if len(Config.Cpuid.AttributeWhitelist) > 0 {
|
||||
for _, k := range Config.Cpuid.AttributeWhitelist {
|
||||
if len(s.config.Cpuid.AttributeWhitelist) > 0 {
|
||||
for _, k := range s.config.Cpuid.AttributeWhitelist {
|
||||
newFilter.keys[k] = struct{}{}
|
||||
}
|
||||
newFilter.whitelist = true
|
||||
} else {
|
||||
for _, k := range Config.Cpuid.AttributeBlacklist {
|
||||
for _, k := range s.config.Cpuid.AttributeBlacklist {
|
||||
newFilter.keys[k] = struct{}{}
|
||||
}
|
||||
newFilter.whitelist = false
|
||||
}
|
||||
cpuidFilter = &newFilter
|
||||
s.cpuidFilter = &newFilter
|
||||
}
|
||||
|
||||
func (f keyFilter) unmask(k string) bool {
|
||||
|
|
|
@ -36,20 +36,41 @@ type FeatureSpec struct {
|
|||
MatchOn []MatchRule `json:"matchOn"`
|
||||
}
|
||||
|
||||
type NFDConfig []FeatureSpec
|
||||
type config []FeatureSpec
|
||||
|
||||
var Config = NFDConfig{}
|
||||
// newDefaultConfig returns a new config with pre-populated defaults
|
||||
func newDefaultConfig() *config {
|
||||
return &config{}
|
||||
}
|
||||
|
||||
// Implements FeatureSource Interface
|
||||
type Source struct{}
|
||||
type Source struct {
|
||||
config *config
|
||||
}
|
||||
|
||||
// Return name of the feature source
|
||||
func (s Source) Name() string { return "custom" }
|
||||
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return newDefaultConfig() }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return s.config }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(conf source.Config) {
|
||||
switch v := conf.(type) {
|
||||
case *config:
|
||||
s.config = v
|
||||
default:
|
||||
log.Printf("PANIC: invalid config type: %T", conf)
|
||||
}
|
||||
}
|
||||
|
||||
// Discover features
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
features := source.Features{}
|
||||
allFeatureConfig := append(getStaticFeatureConfig(), Config...)
|
||||
allFeatureConfig := append(getStaticFeatureConfig(), *s.config...)
|
||||
log.Printf("INFO: Custom features: %+v", allFeatureConfig)
|
||||
// Iterate over features
|
||||
for _, customFeature := range allFeatureConfig {
|
||||
|
|
|
@ -24,6 +24,18 @@ type Source struct{}
|
|||
// Name returns an identifier string for this feature source.
|
||||
func (s Source) Name() string { return "fake" }
|
||||
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return nil }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return nil }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(source.Config) {}
|
||||
|
||||
// Configure method of the FeatureSource interface
|
||||
func (s Source) Configure([]byte) error { return nil }
|
||||
|
||||
// Discover returns feature names for some fake features.
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
// Adding three fake features.
|
||||
|
|
|
@ -28,6 +28,15 @@ type Source struct{}
|
|||
|
||||
func (s Source) Name() string { return "iommu" }
|
||||
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return nil }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return nil }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(source.Config) {}
|
||||
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
features := source.Features{}
|
||||
|
||||
|
|
|
@ -30,27 +30,48 @@ import (
|
|||
)
|
||||
|
||||
// Configuration file options
|
||||
type NFDConfig struct {
|
||||
type Config struct {
|
||||
KconfigFile string
|
||||
ConfigOpts []string `json:"configOpts,omitempty"`
|
||||
}
|
||||
|
||||
var Config = NFDConfig{
|
||||
KconfigFile: "",
|
||||
ConfigOpts: []string{
|
||||
"NO_HZ",
|
||||
"NO_HZ_IDLE",
|
||||
"NO_HZ_FULL",
|
||||
"PREEMPT",
|
||||
},
|
||||
// newDefaultConfig returns a new config with pre-populated defaults
|
||||
func newDefaultConfig() *Config {
|
||||
return &Config{
|
||||
KconfigFile: "",
|
||||
ConfigOpts: []string{
|
||||
"NO_HZ",
|
||||
"NO_HZ_IDLE",
|
||||
"NO_HZ_FULL",
|
||||
"PREEMPT",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Implement FeatureSource interface
|
||||
type Source struct{}
|
||||
type Source struct {
|
||||
config *Config
|
||||
}
|
||||
|
||||
func (s Source) Name() string { return "kernel" }
|
||||
func (s *Source) Name() string { return "kernel" }
|
||||
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return newDefaultConfig() }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return s.config }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(conf source.Config) {
|
||||
switch v := conf.(type) {
|
||||
case *Config:
|
||||
s.config = v
|
||||
default:
|
||||
log.Printf("PANIC: invalid config type: %T", conf)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Source) Discover() (source.Features, error) {
|
||||
features := source.Features{}
|
||||
|
||||
// Read kernel version
|
||||
|
@ -64,13 +85,13 @@ func (s Source) Discover() (source.Features, error) {
|
|||
}
|
||||
|
||||
// Read kconfig
|
||||
kconfig, err := parseKconfig()
|
||||
kconfig, err := s.parseKconfig()
|
||||
if err != nil {
|
||||
log.Printf("ERROR: Failed to read kconfig: %s", err)
|
||||
}
|
||||
|
||||
// Check flags
|
||||
for _, opt := range Config.ConfigOpts {
|
||||
for _, opt := range s.config.ConfigOpts {
|
||||
if val, ok := kconfig[opt]; ok {
|
||||
features["config."+opt] = val
|
||||
}
|
||||
|
@ -137,16 +158,16 @@ func readKconfigGzip(filename string) ([]byte, error) {
|
|||
}
|
||||
|
||||
// Read kconfig into a map
|
||||
func parseKconfig() (map[string]string, error) {
|
||||
func (s *Source) parseKconfig() (map[string]string, error) {
|
||||
kconfig := map[string]string{}
|
||||
raw := []byte(nil)
|
||||
var err error
|
||||
|
||||
// First, try kconfig specified in the config file
|
||||
if len(Config.KconfigFile) > 0 {
|
||||
raw, err = ioutil.ReadFile(Config.KconfigFile)
|
||||
if len(s.config.KconfigFile) > 0 {
|
||||
raw, err = ioutil.ReadFile(s.config.KconfigFile)
|
||||
if err != nil {
|
||||
log.Printf("ERROR: Failed to read kernel config from %s: %s", Config.KconfigFile, err)
|
||||
log.Printf("ERROR: Failed to read kernel config from %s: %s", s.config.KconfigFile, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,15 @@ type Source struct{}
|
|||
|
||||
func (s Source) Name() string { return "local" }
|
||||
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return nil }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return nil }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(source.Config) {}
|
||||
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
featuresFromHooks, err := getFeaturesFromHooks()
|
||||
if err != nil {
|
||||
|
|
|
@ -31,6 +31,15 @@ type Source struct{}
|
|||
// Name returns an identifier string for this feature source.
|
||||
func (s Source) Name() string { return "memory" }
|
||||
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return nil }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return nil }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(source.Config) {}
|
||||
|
||||
// Discover returns feature names for memory: numa if more than one memory node is present.
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
features := source.Features{}
|
||||
|
|
|
@ -34,6 +34,22 @@ func (_m *MockFeatureSource) Discover() (Features, error) {
|
|||
return r0, r1
|
||||
}
|
||||
|
||||
// GetConfig provides a mock function with given fields:
|
||||
func (_m *MockFeatureSource) GetConfig() Config {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 Config
|
||||
if rf, ok := ret.Get(0).(func() Config); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(Config)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Name provides a mock function with given fields:
|
||||
func (_m *MockFeatureSource) Name() string {
|
||||
ret := _m.Called()
|
||||
|
@ -47,3 +63,24 @@ func (_m *MockFeatureSource) Name() string {
|
|||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewConfig provides a mock function with given fields:
|
||||
func (_m *MockFeatureSource) NewConfig() Config {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 Config
|
||||
if rf, ok := ret.Get(0).(func() Config); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(Config)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// SetConfig provides a mock function with given fields: _a0
|
||||
func (_m *MockFeatureSource) SetConfig(_a0 Config) {
|
||||
_m.Called(_a0)
|
||||
}
|
||||
|
|
|
@ -34,6 +34,15 @@ type Source struct{}
|
|||
// Name returns an identifier string for this feature source.
|
||||
func (s Source) Name() string { return "network" }
|
||||
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return nil }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return nil }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(source.Config) {}
|
||||
|
||||
// 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() (source.Features, error) {
|
||||
features := source.Features{}
|
||||
|
|
|
@ -24,6 +24,15 @@ type Source struct{}
|
|||
// Name returns an identifier string for this feature source.
|
||||
func (s Source) Name() string { return "panic_fake" }
|
||||
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return nil }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return nil }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(source.Config) {}
|
||||
|
||||
// Discover calls panic().
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
panic("fake panic error")
|
||||
|
|
|
@ -25,22 +25,43 @@ import (
|
|||
pciutils "sigs.k8s.io/node-feature-discovery/source/internal"
|
||||
)
|
||||
|
||||
type NFDConfig struct {
|
||||
type Config struct {
|
||||
DeviceClassWhitelist []string `json:"deviceClassWhitelist,omitempty"`
|
||||
DeviceLabelFields []string `json:"deviceLabelFields,omitempty"`
|
||||
}
|
||||
|
||||
var Config = NFDConfig{
|
||||
DeviceClassWhitelist: []string{"03", "0b40", "12"},
|
||||
DeviceLabelFields: []string{"class", "vendor"},
|
||||
// newDefaultConfig returns a new config with pre-populated defaults
|
||||
func newDefaultConfig() *Config {
|
||||
return &Config{
|
||||
DeviceClassWhitelist: []string{"03", "0b40", "12"},
|
||||
DeviceLabelFields: []string{"class", "vendor"},
|
||||
}
|
||||
}
|
||||
|
||||
// Implement FeatureSource interface
|
||||
type Source struct{}
|
||||
type Source struct {
|
||||
config *Config
|
||||
}
|
||||
|
||||
// Return name of the feature source
|
||||
func (s Source) Name() string { return "pci" }
|
||||
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return newDefaultConfig() }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return s.config }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(conf source.Config) {
|
||||
switch v := conf.(type) {
|
||||
case *Config:
|
||||
s.config = v
|
||||
default:
|
||||
log.Printf("PANIC: invalid config type: %T", conf)
|
||||
}
|
||||
}
|
||||
|
||||
// Discover features
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
features := source.Features{}
|
||||
|
@ -48,7 +69,7 @@ func (s Source) Discover() (source.Features, error) {
|
|||
// Construct a device label format, a sorted list of valid attributes
|
||||
deviceLabelFields := []string{}
|
||||
configLabelFields := map[string]bool{}
|
||||
for _, field := range Config.DeviceLabelFields {
|
||||
for _, field := range s.config.DeviceLabelFields {
|
||||
configLabelFields[field] = true
|
||||
}
|
||||
|
||||
|
@ -87,7 +108,7 @@ func (s Source) Discover() (source.Features, error) {
|
|||
|
||||
// Iterate over all device classes
|
||||
for class, classDevs := range devs {
|
||||
for _, white := range Config.DeviceClassWhitelist {
|
||||
for _, white := range s.config.DeviceClassWhitelist {
|
||||
if strings.HasPrefix(class, strings.ToLower(white)) {
|
||||
for _, dev := range classDevs {
|
||||
devLabel := ""
|
||||
|
|
|
@ -39,4 +39,16 @@ type FeatureSource interface {
|
|||
|
||||
// Discover returns discovered features for this node.
|
||||
Discover() (Features, error)
|
||||
|
||||
// NewConfig returns a new default config of the source
|
||||
NewConfig() Config
|
||||
|
||||
// GetConfig returns the effective configuration of the source
|
||||
GetConfig() Config
|
||||
|
||||
// SetConfig changes the effective configuration of the source
|
||||
SetConfig(Config)
|
||||
}
|
||||
|
||||
type Config interface {
|
||||
}
|
||||
|
|
|
@ -29,6 +29,15 @@ type Source struct{}
|
|||
// Name returns an identifier string for this feature source.
|
||||
func (s Source) Name() string { return "storage" }
|
||||
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return nil }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return nil }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(source.Config) {}
|
||||
|
||||
// Discover returns feature names for storage: nonrotationaldisk if any SSD drive present.
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
features := source.Features{}
|
||||
|
|
|
@ -36,6 +36,15 @@ type Source struct{}
|
|||
|
||||
func (s Source) Name() string { return "system" }
|
||||
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return nil }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return nil }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(source.Config) {}
|
||||
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
features := source.Features{}
|
||||
|
||||
|
|
|
@ -25,25 +25,46 @@ import (
|
|||
usbutils "sigs.k8s.io/node-feature-discovery/source/internal"
|
||||
)
|
||||
|
||||
type NFDConfig struct {
|
||||
type Config struct {
|
||||
DeviceClassWhitelist []string `json:"deviceClassWhitelist,omitempty"`
|
||||
DeviceLabelFields []string `json:"deviceLabelFields,omitempty"`
|
||||
}
|
||||
|
||||
var Config = NFDConfig{
|
||||
// Whitelist specific USB classes: https://www.usb.org/defined-class-codes
|
||||
// By default these include classes where different accelerators are typically mapped:
|
||||
// Video (0e), Miscellaneous (ef), Application Specific (fe), and Vendor Specific (ff).
|
||||
DeviceClassWhitelist: []string{"0e", "ef", "fe", "ff"},
|
||||
DeviceLabelFields: []string{"class", "vendor", "device"},
|
||||
// newDefaultConfig returns a new config with pre-populated defaults
|
||||
func newDefaultConfig() *Config {
|
||||
return &Config{
|
||||
// Whitelist specific USB classes: https://www.usb.org/defined-class-codes
|
||||
// By default these include classes where different accelerators are typically mapped:
|
||||
// Video (0e), Miscellaneous (ef), Application Specific (fe), and Vendor Specific (ff).
|
||||
DeviceClassWhitelist: []string{"0e", "ef", "fe", "ff"},
|
||||
DeviceLabelFields: []string{"class", "vendor", "device"},
|
||||
}
|
||||
}
|
||||
|
||||
// Implement FeatureSource interface
|
||||
type Source struct{}
|
||||
type Source struct {
|
||||
config *Config
|
||||
}
|
||||
|
||||
// Return name of the feature source
|
||||
func (s Source) Name() string { return "usb" }
|
||||
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return newDefaultConfig() }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return s.config }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(conf source.Config) {
|
||||
switch v := conf.(type) {
|
||||
case *Config:
|
||||
s.config = v
|
||||
default:
|
||||
log.Printf("PANIC: invalid config type: %T", conf)
|
||||
}
|
||||
}
|
||||
|
||||
// Discover features
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
features := source.Features{}
|
||||
|
@ -51,7 +72,7 @@ func (s Source) Discover() (source.Features, error) {
|
|||
// Construct a device label format, a sorted list of valid attributes
|
||||
deviceLabelFields := []string{}
|
||||
configLabelFields := map[string]bool{}
|
||||
for _, field := range Config.DeviceLabelFields {
|
||||
for _, field := range s.config.DeviceLabelFields {
|
||||
configLabelFields[field] = true
|
||||
}
|
||||
|
||||
|
@ -86,7 +107,7 @@ func (s Source) Discover() (source.Features, error) {
|
|||
|
||||
// Iterate over all device classes
|
||||
for class, classDevs := range devs {
|
||||
for _, white := range Config.DeviceClassWhitelist {
|
||||
for _, white := range s.config.DeviceClassWhitelist {
|
||||
if strings.HasPrefix(class, strings.ToLower(white)) {
|
||||
for _, dev := range classDevs {
|
||||
devLabel := ""
|
||||
|
|
Loading…
Add table
Reference in a new issue