Skip to content

Commit

Permalink
Add noLabel field to rule spec and support backreferences
Browse files Browse the repository at this point in the history
Support backreferencing of output values from previous rules. Enables
complex setups where custom features are further combined together to
form even more sophisticated higher level labels. The output of
preceding rules are available as a special 'rule.matched' feature in
matchAny and matchAll directives. If referencing rules accross multiple
configs/CRDs care must be taken with the ordering.

Processing order of rules in nfd-worker:
1. Static rules
2. Files from /etc/kubernetes/node-feature-discovery/custom.d/
   in alphabetical order. Subdirectories are processed by reading their
   files in alphabetical order.
3. Custom rules from main nfd-worker.conf

In nfd-master the CRs are processed in alphabetical order (based on
their metadata.name).

Also, add 'noLabel' boolean field to the rulespec. Enabling this causes
the rule to not generate any node labels. This may be desired in schemes
where the output of certain rules is only used as an intermediate
variable for other rules.

An example setup:

  - name: kernel-feature
    matchAny:
    - kernel.version:
        major: {op: Gt, value: ["4"]}

  - name: nolabel-feature
    noLabel: true
    matchAll:
    - cpu.cpuid:
        AVX512F: {op: Exists}
      pci.device:
        vendor: {op: In, value: ["8086"]}
        device: {op: In, value: ["1234", "1235"]}

  - name: top-level-feature
    matchAll:
    - rule.matched:
        kernel-feature: "true"
        nolabel-feature: "true"
  • Loading branch information
marquiz committed Jul 8, 2021
1 parent d89cded commit 47d93b9
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 5 deletions.
6 changes: 6 additions & 0 deletions crd/nfd.kubernetes.test_labelrules.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@ spec:
name:
description: Name of the label to be generated.
type: string
noLabel:
description: NoLabel disables the label output of the rule.
Output value(s) of the matching process are only added as
values under a special "MATCHES" key for subsequent rules
to use as input features for matching.
type: boolean
value:
description: Value of the label, optional.
type: string
Expand Down
11 changes: 11 additions & 0 deletions custom-test.conf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,14 @@ sources:
matchAny:
- cpu.cpuid:
"*": {op: InRegexp, value: ["^SSE"]}

- name: nolabel-test-1
matchAll:
- cpu.cpuid:
noLabel: true

- name: BACKREF-test-1
matchAll:
- rule.matched:
system-test-1: "true"
nolabel-test-1: "true"
14 changes: 14 additions & 0 deletions pkg/api/feature/feature.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,17 @@ func NewInstanceFeatures() *InstanceFeatures { return &InstanceFeatures{} }
func NewInstanceFeature() *InstanceFeature {
return &InstanceFeature{Attributes: make(map[string]string)}
}

func InsertValues(f Features, domain, feature string, values map[string]string) {
if _, ok := f[domain]; !ok {
f[domain] = NewDomainFeatures()
}
if _, ok := f[domain].Values[feature]; !ok {
f[domain].Values[feature] = *NewValueFeatures()
}

features := f[domain].Values[feature].Features
for k, v := range values {
features[k] = v
}
}
2 changes: 1 addition & 1 deletion pkg/apis/nfd/v1alpha1/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func (m *MatchExpression) Validate() error {
return fmt.Errorf("Values should be empty for Op %q (got %v)", m.Op, m.Value)
}
case MatchGt, MatchLt:
if len(m.Value) != 0 {
if len(m.Value) != 1 {
return fmt.Errorf("Values should contain exactly one element for Op %q (got %v)", m.Op, m.Value)
}
default:
Expand Down
15 changes: 15 additions & 0 deletions pkg/apis/nfd/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ type Rule struct {
// +optional
Value *string `json:"value,omitempty"`

// NoLabel disables the label output of the rule. Output value(s) of the
// matching process are only added as values under a special "MATCHES" key
// for subsequent rules to use as input features for matching.
// +optional
NoLabel bool `json:"noLabel"`

// MatchAny specifies a list of expression sets one of which must match
// +optional
MatchAny []MatchRule `json:"matchAny"`
Expand Down Expand Up @@ -124,3 +130,12 @@ const (
// MatchAllNames is a special key in MatchExpressionSet to use field names
// (keys from the input) instead of values when matching.
const MatchAllNames = "*"

const (
// RuleBackrefDomain is the special feature domain for backreferencing
// output of preceding rules.
RuleBackrefDomain = "rule"
// RuleBackrefFeature is the special feature name for backreferencing
// output of preceding rules.
RuleBackrefFeature = "matched"
)
16 changes: 14 additions & 2 deletions pkg/nfd-master/nfd-master.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ import (
"k8s.io/client-go/tools/clientcmd"
"k8s.io/klog/v2"

"sigs.k8s.io/node-feature-discovery/pkg/api/feature"
"sigs.k8s.io/node-feature-discovery/pkg/apihelper"
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
pb "sigs.k8s.io/node-feature-discovery/pkg/labeler"
"sigs.k8s.io/node-feature-discovery/pkg/utils"
"sigs.k8s.io/node-feature-discovery/pkg/version"
Expand Down Expand Up @@ -454,6 +456,10 @@ func (m *nfdMaster) crLabels(r *pb.SetLabelsRequest) map[string]string {

l := make(map[string]string)
ruleSpecs, err := m.nfdController.lister.List(labels.Everything())
sort.Slice(ruleSpecs, func(i, j int) bool {
return ruleSpecs[i].Name < ruleSpecs[j].Name
})

if err != nil {
klog.Errorf("failed to list LabelRule resources: %w", err)
return nil
Expand All @@ -474,10 +480,16 @@ func (m *nfdMaster) crLabels(r *pb.SetLabelsRequest) map[string]string {
klog.Errorf("failed to process Rule %q: %w", rule.Name, err)
continue
}
for k, v := range ruleOut {
l[k] = v

if !rule.NoLabel {
for k, v := range ruleOut {
l[k] = v
}
}
utils.KlogDump(1, "", " ", ruleOut)

// Feed back rule output to features map for subsequent rules to match
feature.InsertValues(r.Features, nfdv1alpha1.RuleBackrefDomain, nfdv1alpha1.RuleBackrefFeature, ruleOut)
}
}

Expand Down
9 changes: 7 additions & 2 deletions source/custom/custom.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,13 @@ func (s *customSource) GetLabels() (source.FeatureLabels, error) {
klog.Errorf("failed to discover feature: %q: %s", spec.Name, err.Error())
continue
}
for k, v := range ruleOut {
labels[k] = v
if !spec.NoLabel {
for k, v := range ruleOut {
labels[k] = v
}
}
// Feed back rule output to features map for subsequent rules to match
feature.InsertValues(domainFeatures, nfdv1alpha1.RuleBackrefDomain, nfdv1alpha1.RuleBackrefFeature, ruleOut)
}
return labels, nil
}
Expand Down Expand Up @@ -154,6 +158,7 @@ func (s *FeatureSpec) Match(features map[string]*feature.DomainFeatures) (map[st
return nil, nil
}
}

return ret, nil
}

Expand Down

0 comments on commit 47d93b9

Please sign in to comment.