mirror of
https://github.com/kubernetes-sigs/node-feature-discovery.git
synced 2024-12-14 11:57:51 +00:00
Implement PCI feature source
This feature source detects the presence of PCI devices. At the moment, it only advertises GPUs and accelerator cards, i.e. device classes 0x03, 0x0b40 and 0x12. The label format is: node.alpha.kubernetes-incubator.io/nfd-pci-<device label>.present where <device label> is composed of raw PCI IDs: <class id>_<vendor id>
This commit is contained in:
parent
244e049729
commit
74d6993e9b
4 changed files with 133 additions and 4 deletions
18
README.md
18
README.md
|
@ -54,7 +54,7 @@ node-feature-discovery.
|
|||
will override settings read from the config file.
|
||||
[Default: ]
|
||||
--sources=<sources> Comma separated list of feature sources.
|
||||
[Default: cpuid,iommu,memory,network,pstate,rdt,selinux,storage]
|
||||
[Default: cpuid,iommu,memory,network,pci,pstate,rdt,selinux,storage]
|
||||
--no-publish Do not publish discovered features to the
|
||||
cluster-local Kubernetes API server.
|
||||
--label-whitelist=<pattern> Regular expression to filter label names to
|
||||
|
@ -115,6 +115,7 @@ the only label value published for features is the string `"true"`._
|
|||
"node.alpha.kubernetes-incubator.io/nfd-iommu-<feature-name>": "true",
|
||||
"node.alpha.kubernetes-incubator.io/nfd-memory-<feature-name>": "true",
|
||||
"node.alpha.kubernetes-incubator.io/nfd-network-<feature-name>": "true",
|
||||
"node.alpha.kubernetes-incubator.io/nfd-pci-<device label>.present": "true",
|
||||
"node.alpha.kubernetes-incubator.io/nfd-pstate-<feature-name>": "true",
|
||||
"node.alpha.kubernetes-incubator.io/nfd-rdt-<feature-name>": "true",
|
||||
"node.alpha.kubernetes-incubator.io/nfd-selinux-<feature-name>": "true",
|
||||
|
@ -176,6 +177,21 @@ such as restricting discovered features with the --label-whitelist option._
|
|||
| sriov | capable | [Single Root Input/Output Virtualization][sriov] (SR-IOV) enabled Network Interface Card(s) present
|
||||
| <br> | configured | SR-IOV virtual functions have been configured
|
||||
|
||||
### PCI Features
|
||||
|
||||
| Feature | Attribute | Description |
|
||||
| -------------------- | --------- | ----------------------------------------- |
|
||||
| <device label> | present | PCI device is detected
|
||||
|
||||
The `<device label>` part is composed of raw PCI IDs, and has the following
|
||||
format: `<class>_<vendor>`. E.g.
|
||||
```
|
||||
node.alpha.kubernetes-incubator.io/nfd-pci-1200_8086.present=true
|
||||
```
|
||||
|
||||
Only device classes (0x)03, (0x)0b40 and (0x)12, i.e. GPUs, co-processors and
|
||||
accelerator cards are deteted.
|
||||
|
||||
### RDT (Intel Resource Director Technology) Features
|
||||
|
||||
| Feature name | Description |
|
||||
|
|
4
main.go
4
main.go
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/kubernetes-incubator/node-feature-discovery/source/memory"
|
||||
"github.com/kubernetes-incubator/node-feature-discovery/source/network"
|
||||
"github.com/kubernetes-incubator/node-feature-discovery/source/panic_fake"
|
||||
"github.com/kubernetes-incubator/node-feature-discovery/source/pci"
|
||||
"github.com/kubernetes-incubator/node-feature-discovery/source/pstate"
|
||||
"github.com/kubernetes-incubator/node-feature-discovery/source/rdt"
|
||||
"github.com/kubernetes-incubator/node-feature-discovery/source/selinux"
|
||||
|
@ -163,7 +164,7 @@ func argsParse(argv []string) (args Args) {
|
|||
will override settings read from the config file.
|
||||
[Default: ]
|
||||
--sources=<sources> Comma separated list of feature sources.
|
||||
[Default: cpuid,iommu,memory,network,pstate,rdt,selinux,storage]
|
||||
[Default: cpuid,iommu,memory,network,pci,pstate,rdt,selinux,storage]
|
||||
--no-publish Do not publish discovered features to the
|
||||
cluster-local Kubernetes API server.
|
||||
--label-whitelist=<pattern> Regular expression to filter label names to
|
||||
|
@ -242,6 +243,7 @@ func configureParameters(sourcesWhiteList []string, labelWhiteListStr string) (e
|
|||
memory.Source{},
|
||||
network.Source{},
|
||||
panic_fake.Source{},
|
||||
pci.Source{},
|
||||
pstate.Source{},
|
||||
rdt.Source{},
|
||||
selinux.Source{},
|
||||
|
|
|
@ -137,7 +137,7 @@ func TestArgsParse(t *testing.T) {
|
|||
So(args.sleepInterval, ShouldEqual, 60*time.Second)
|
||||
So(args.noPublish, ShouldBeTrue)
|
||||
So(args.oneshot, ShouldBeTrue)
|
||||
So(args.sources, ShouldResemble, []string{"cpuid", "iommu", "memory", "network", "pstate", "rdt", "selinux", "storage"})
|
||||
So(args.sources, ShouldResemble, []string{"cpuid", "iommu", "memory", "network", "pci", "pstate", "rdt", "selinux", "storage"})
|
||||
So(len(args.labelWhiteList), ShouldEqual, 0)
|
||||
})
|
||||
})
|
||||
|
@ -159,7 +159,7 @@ func TestArgsParse(t *testing.T) {
|
|||
|
||||
Convey("args.labelWhiteList is set to appropriate value and args.sources is set to default value", func() {
|
||||
So(args.noPublish, ShouldBeFalse)
|
||||
So(args.sources, ShouldResemble, []string{"cpuid", "iommu", "memory", "network", "pstate", "rdt", "selinux", "storage"})
|
||||
So(args.sources, ShouldResemble, []string{"cpuid", "iommu", "memory", "network", "pci", "pstate", "rdt", "selinux", "storage"})
|
||||
So(args.labelWhiteList, ShouldResemble, ".*rdt.*")
|
||||
})
|
||||
})
|
||||
|
|
111
source/pci/pci.go
Normal file
111
source/pci/pci.go
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package pci
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type pciDeviceInfo map[string]string
|
||||
|
||||
var logger = log.New(os.Stderr, "", log.LstdFlags)
|
||||
|
||||
var deviceClassWhitelist = []string{"03", "0b40", "12"}
|
||||
|
||||
// Implement FeatureSource interface
|
||||
type Source struct{}
|
||||
|
||||
// Return name of the feature source
|
||||
func (s Source) Name() string { return "pci" }
|
||||
|
||||
// Discover features
|
||||
func (s Source) Discover() ([]string, error) {
|
||||
features := map[string]bool{}
|
||||
|
||||
devs, err := detectPci()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to detect PCI devices: %s", err.Error())
|
||||
}
|
||||
|
||||
for class, classDevs := range devs {
|
||||
for _, white := range deviceClassWhitelist {
|
||||
if strings.HasPrefix(class, white) {
|
||||
for _, dev := range classDevs {
|
||||
features[fmt.Sprintf("%s_%s.present", dev["class"], dev["vendor"])] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
feature_list := []string{}
|
||||
for feature := range features {
|
||||
feature_list = append(feature_list, feature)
|
||||
}
|
||||
|
||||
return feature_list, nil
|
||||
}
|
||||
|
||||
// Read information of one PCI device
|
||||
func readDevInfo(devPath string) (pciDeviceInfo, error) {
|
||||
info := pciDeviceInfo{}
|
||||
|
||||
attrs := []string{"class", "vendor", "device", "subsystem_vendor", "subsystem_device"}
|
||||
for _, attr := range attrs {
|
||||
data, err := ioutil.ReadFile(path.Join(devPath, attr))
|
||||
if err != nil {
|
||||
return info, fmt.Errorf("Failed to read device %s: %s", attr, err)
|
||||
}
|
||||
// Strip whitespace and '0x' prefix
|
||||
info[attr] = strings.TrimSpace(strings.TrimPrefix(string(data), "0x"))
|
||||
|
||||
if attr == "class" && len(info[attr]) > 4 {
|
||||
// Take four first characters, so that the programming
|
||||
// interface identifier gets stripped from the raw class code
|
||||
info[attr] = info[attr][0:4]
|
||||
}
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// List available PCI devices
|
||||
func detectPci() (map[string][]pciDeviceInfo, error) {
|
||||
const basePath = "/sys/bus/pci/devices/"
|
||||
devInfo := make(map[string][]pciDeviceInfo)
|
||||
|
||||
devices, err := ioutil.ReadDir(basePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Iterate over devices
|
||||
for _, device := range devices {
|
||||
info, err := readDevInfo(path.Join(basePath, device.Name()))
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
continue
|
||||
}
|
||||
class := info["class"]
|
||||
devInfo[class] = append(devInfo[class], info)
|
||||
}
|
||||
|
||||
return devInfo, nil
|
||||
}
|
Loading…
Reference in a new issue