1
0
Fork 0
mirror of https://github.com/kubernetes-sigs/node-feature-discovery.git synced 2025-04-23 20:57:10 +00:00

Merge pull request from marquiz/devel/wildcard-rule-ng

apis/nfd: add matchName field in feature matcher terms
This commit is contained in:
Kubernetes Prow Robot 2023-12-15 12:30:00 +01:00 committed by GitHub
commit 119e2a374b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 901 additions and 66 deletions

View file

@ -94,6 +94,13 @@ spec:
vendor: {op: In, value: ["8086"]}
class: {op: In, value: ["02"]}
- name: "avx wildcard rule"
labels:
"my-avx-feature": "true"
matchFeatures:
- feature: cpu.cpuid
matchName: {op: InRegexp, value: ["^AVX512"]}
# The following features demonstreate label templating capabilities
- name: "my system template feature"
labelsTemplate: |
@ -143,3 +150,11 @@ spec:
matchExpressions:
my.kernel.feature: {op: IsTrue}
my.dummy.var: {op: Gt, value: ["0"]}
- name: "kconfig template rule"
labelsTemplate: |
{{ range .kernel.config }}kconfig-{{ .Name }}={{ .Value }}
{{ end }}
matchFeatures:
- feature: kernel.config
matchName: {op: In, value: ["SWAP", "X86", "ARM"]}

View file

@ -190,6 +190,8 @@ spec:
in the feature set.
properties:
feature:
description: Feature is the name of the feature
set to match against.
type: string
matchExpressions:
additionalProperties:
@ -229,13 +231,46 @@ spec:
required:
- op
type: object
description: MatchExpressionSet contains a set of
MatchExpressions, each of which is evaluated against
a set of input values.
description: MatchExpressions is the set of per-element
expressions evaluated. These match against the
value of the specified elements.
type: object
matchName:
description: MatchName in an expression that is
matched against the name of each element in the
feature set.
properties:
op:
description: Op is the operator to be applied.
enum:
- In
- NotIn
- InRegexp
- Exists
- DoesNotExist
- Gt
- Lt
- GtLt
- IsTrue
- IsFalse
type: string
value:
description: Value is the list of values that
the operand evaluates the input against. Value
should be empty if the operator is Exists,
DoesNotExist, IsTrue or IsFalse. Value should
contain exactly one element if the operator
is Gt or Lt and exactly two elements if the
operator is GtLt. In other cases Value should
contain at least one element.
items:
type: string
type: array
required:
- op
type: object
required:
- feature
- matchExpressions
type: object
type: array
required:
@ -251,6 +286,8 @@ spec:
are evaluated against each element in the feature set.
properties:
feature:
description: Feature is the name of the feature set to
match against.
type: string
matchExpressions:
additionalProperties:
@ -288,12 +325,44 @@ spec:
required:
- op
type: object
description: MatchExpressionSet contains a set of MatchExpressions,
each of which is evaluated against a set of input values.
description: MatchExpressions is the set of per-element
expressions evaluated. These match against the value
of the specified elements.
type: object
matchName:
description: MatchName in an expression that is matched
against the name of each element in the feature set.
properties:
op:
description: Op is the operator to be applied.
enum:
- In
- NotIn
- InRegexp
- Exists
- DoesNotExist
- Gt
- Lt
- GtLt
- IsTrue
- IsFalse
type: string
value:
description: Value is the list of values that the
operand evaluates the input against. Value should
be empty if the operator is Exists, DoesNotExist,
IsTrue or IsFalse. Value should contain exactly
one element if the operator is Gt or Lt and exactly
two elements if the operator is GtLt. In other cases
Value should contain at least one element.
items:
type: string
type: array
required:
- op
type: object
required:
- feature
- matchExpressions
type: object
type: array
name:

View file

@ -174,6 +174,13 @@
# vendor: {op: In, value: ["8086"]}
# class: {op: In, value: ["02"]}
#
# - name: "avx wildcard rule"
# labels:
# "my-avx-feature": "true"
# matchFeatures:
# - feature: cpu.cpuid
# matchName: {op: InRegexp, value: ["^AVX512"]}
#
# # The following features demonstreate label templating capabilities
# - name: "my template rule"
# labelsTemplate: |
@ -224,3 +231,10 @@
# vendor.io/my.kernel.feature: {op: IsTrue}
# my.dummy.var: {op: Gt, value: ["0"]}
#
# - name: "kconfig template rule"
# labelsTemplate: |
# {{ range .kernel.config }}kconfig-{{ .Name }}={{ .Value }}
# {{ end }}
# matchFeatures:
# - feature: kernel.config
# matchName: {op: In, value: ["SWAP", "X86", "ARM"]}

View file

@ -190,6 +190,8 @@ spec:
in the feature set.
properties:
feature:
description: Feature is the name of the feature
set to match against.
type: string
matchExpressions:
additionalProperties:
@ -229,13 +231,46 @@ spec:
required:
- op
type: object
description: MatchExpressionSet contains a set of
MatchExpressions, each of which is evaluated against
a set of input values.
description: MatchExpressions is the set of per-element
expressions evaluated. These match against the
value of the specified elements.
type: object
matchName:
description: MatchName in an expression that is
matched against the name of each element in the
feature set.
properties:
op:
description: Op is the operator to be applied.
enum:
- In
- NotIn
- InRegexp
- Exists
- DoesNotExist
- Gt
- Lt
- GtLt
- IsTrue
- IsFalse
type: string
value:
description: Value is the list of values that
the operand evaluates the input against. Value
should be empty if the operator is Exists,
DoesNotExist, IsTrue or IsFalse. Value should
contain exactly one element if the operator
is Gt or Lt and exactly two elements if the
operator is GtLt. In other cases Value should
contain at least one element.
items:
type: string
type: array
required:
- op
type: object
required:
- feature
- matchExpressions
type: object
type: array
required:
@ -251,6 +286,8 @@ spec:
are evaluated against each element in the feature set.
properties:
feature:
description: Feature is the name of the feature set to
match against.
type: string
matchExpressions:
additionalProperties:
@ -288,12 +325,44 @@ spec:
required:
- op
type: object
description: MatchExpressionSet contains a set of MatchExpressions,
each of which is evaluated against a set of input values.
description: MatchExpressions is the set of per-element
expressions evaluated. These match against the value
of the specified elements.
type: object
matchName:
description: MatchName in an expression that is matched
against the name of each element in the feature set.
properties:
op:
description: Op is the operator to be applied.
enum:
- In
- NotIn
- InRegexp
- Exists
- DoesNotExist
- Gt
- Lt
- GtLt
- IsTrue
- IsFalse
type: string
value:
description: Value is the list of values that the
operand evaluates the input against. Value should
be empty if the operator is Exists, DoesNotExist,
IsTrue or IsFalse. Value should contain exactly
one element if the operator is Gt or Lt and exactly
two elements if the operator is GtLt. In other cases
Value should contain at least one element.
items:
type: string
type: array
required:
- op
type: object
required:
- feature
- matchExpressions
type: object
type: array
name:

View file

@ -312,6 +312,13 @@ worker:
# vendor: {op: In, value: ["8086"]}
# class: {op: In, value: ["02"]}
#
# - name: "avx wildcard rule"
# labels:
# "my-avx-feature": "true"
# matchFeatures:
# - feature: cpu.cpuid
# matchName: {op: InRegexp, value: ["^AVX512"]}
#
# # The following features demonstreate label templating capabilities
# - name: "my template rule"
# labelsTemplate: |
@ -362,6 +369,13 @@ worker:
# vendor.io/my.kernel.feature: {op: IsTrue}
# my.dummy.var: {op: Gt, value: ["0"]}
#
# - name: "kconfig template rule"
# labelsTemplate: |
# {{ range .kernel.config }}kconfig-{{ .Name }}={{ .Value }}
# {{ end }}
# matchFeatures:
# - feature: kernel.config
# matchName: {op: In, value: ["SWAP", "X86", "ARM"]}
### <NFD-WORKER-CONF-END-DO-NOT-REMOVE>
metricsPort: 8081

View file

@ -761,16 +761,46 @@ of them must match for the rule to trigger.
value:
- <value-1>
- ...
matchName:
op: <op>
value:
- <value-1>
- ...
```
The `.matchFeatures[].feature` field specifies the feature against which to
match.
The `.matchFeatures[].feature` field specifies the feature which to evaluate.
The `.matchFeatures[].matchExpressions` field specifies a map of expressions
which to evaluate against the elements of the feature.
> **NOTE:**If both [`matchExpressions`](#matchexpressions) and
> [`matchName`](#matchname) are specified, they both must match.
In each MatchExpression `op` specifies the operator to apply. Valid values are
described below.
##### matchExpressions
The `.matchFeatures[].matchExpressions` field is used to match against the
value(s) of a feature. The `matchExpressions` field consists of a set of
expressions, each of which is evaluated against all elements of the specified
feature.
```yaml
matchExpressions:
<key>:
op: <op>
value:
- <value-1>
- ...
```
In each MatchExpression the `key` specifies the name of of the feature element
(*flag* and *attribute* features) or name of the attribute (*instance*
features) which to look for. The behavior of MatchExpression depends on the
[feature type](#feature-types):
- for *flag* and *attribute* features the MatchExpression operates on the
feature element whose name matches the `<key>`
- for *instance* features all MatchExpressions are evaluated against the
attributes of each instance separately
The `op` field specifies the operator to apply. Valid values are described
below.
| Operator | Number of values | Matches when |
| --------------- | ---------------- | ----------- |
@ -788,11 +818,57 @@ described below.
The `value` field of MatchExpression is a list of string arguments to the
operator.
The behavior of MatchExpression depends on the [feature type](#feature-types):
for *flag* and *attribute* features the MatchExpression operates on the feature
element whose name matches the `<key>`. However, for *instance* features all
MatchExpressions are evaluated against the attributes of each instance
separately.
##### matchName
The `.matchFeatures[].matchName` field is used to match against the
name(s) of a feature (whereas the [`matchExpressions`](#matchexpressions) field
matches against the value(s). The `matchName` field consists of a single
expression which is evaulated against the name of each element of the specified
feature.
```yaml
matchName:
op: <op>
value:
- <value-1>
- ...
```
The behavior of `matchName` depends on the [feature type](#feature-types):
- for *flag* and *attribute* features the expression is evaluated against the
name of each element
- for *instance* features the expression is evaluated against the name of
each attribute, for each element (instance) separately (matches if the
attributes of any of the elements satisfy the expression)
The `op` field specifies the operator to apply. Same operators as for
[`matchExpressions`](#matchexpressions) above are available.
| Operator | Number of values | Matches |
| --------------- | ---------------- | ----------- |
| `In` | 1 or greater | All name is equal to one of the values |
| `NotIn` | 1 or greater | All name that is not equal to any of the values |
| `InRegexp` | 1 or greater | All name that matches any of the values (treated as regexps) |
| `Exists` | 0 | All elements |
Other operators are not practical with `matchName` (`DoesNotExist` never
matches; `Gt`,`Lt` and `GtLt` are only usable if feature names are integers;
`IsTrue` and `IsFalse` are only usable if the feature name is `true` or
`false`).
The `value` field is a list of string arguments to the operator.
An example:
```yaml
matchFeatures:
- feature: cpu.cpuid
matchName: {op: InRegexp, value: ["^AVX"]}
```
The snippet above would match if any CPUID feature starting with AVX is present
(e.g. AVX1 or AVX2 or AVX512F etc).
#### matchAny
@ -992,6 +1068,10 @@ feature:
```
<!-- {% endraw %} -->
> **NOTE:**If both `matchExpressions` and `matchName` for a feature matcher
> term (see [`matchFeatures`](#matchfeatures)) is specified, the list of
> matched features (for the template engine) is the union from both of these.
<!-- note #2 -->
> **NOTE:** In case of matchAny is specified, the template is executed
> separately against each individual `matchFeatures` field and the final set of
> labels will be superset of all these separate template expansions. E.g.

View file

@ -258,6 +258,88 @@ func (m *MatchExpression) MatchValues(name string, values map[string]string) (bo
return matched, nil
}
// MatchKeyNames evaluates the MatchExpression against names of a set of key features.
func (m *MatchExpression) MatchKeyNames(keys map[string]Nil) (bool, []MatchedElement, error) {
ret := []MatchedElement{}
for k := range keys {
if match, err := m.Match(true, k); err != nil {
return false, nil, err
} else if match {
ret = append(ret, MatchedElement{"Name": k})
}
}
// Sort for reproducible output
sort.Slice(ret, func(i, j int) bool { return ret[i]["Name"] < ret[j]["Name"] })
if klogV3 := klog.V(3); klogV3.Enabled() {
mk := make([]string, len(ret))
for i, v := range ret {
mk[i] = v["Name"]
}
mkMsg := strings.Join(mk, ", ")
if klogV4 := klog.V(4); klogV4.Enabled() {
k := make([]string, 0, len(keys))
for n := range keys {
k = append(k, n)
}
sort.Strings(k)
klogV3.InfoS("matched (key) names", "matchResult", mkMsg, "matchOp", m.Op, "matchValue", m.Value, "inputKeys", k)
} else {
klogV3.InfoS("matched (key) names", "matchResult", mkMsg, "matchOp", m.Op, "matchValue", m.Value)
}
}
return len(ret) > 0, ret, nil
}
// MatchValueNames evaluates the MatchExpression against names of a set of value features.
func (m *MatchExpression) MatchValueNames(values map[string]string) (bool, []MatchedElement, error) {
ret := []MatchedElement{}
for k, v := range values {
if match, err := m.Match(true, k); err != nil {
return false, nil, err
} else if match {
ret = append(ret, MatchedElement{"Name": k, "Value": v})
}
}
// Sort for reproducible output
sort.Slice(ret, func(i, j int) bool { return ret[i]["Name"] < ret[j]["Name"] })
if klogV3 := klog.V(3); klogV3.Enabled() {
mk := make([]string, len(ret))
for i, v := range ret {
mk[i] = v["Name"]
}
mkMsg := strings.Join(mk, ", ")
if klogV4 := klog.V(4); klogV4.Enabled() {
klogV3.InfoS("matched (value) names", "matchResult", mkMsg, "matchOp", m.Op, "matchValue", m.Value, "inputValues", values)
} else {
klogV3.InfoS("matched (value) names", "matchResult", mkMsg, "matchOp", m.Op, "matchValue", m.Value)
}
}
return len(ret) > 0, ret, nil
}
// MatchInstanceAttributeNames evaluates the MatchExpression against a set of
// instance features, matching against the names of their attributes.
func (m *MatchExpression) MatchInstanceAttributeNames(instances []InstanceFeature) ([]MatchedElement, error) {
ret := []MatchedElement{}
for _, i := range instances {
if match, _, err := m.MatchValueNames(i.Attributes); err != nil {
return nil, err
} else if match {
ret = append(ret, i.Attributes)
}
}
return ret, nil
}
// matchExpression is a helper type for unmarshalling MatchExpression
type matchExpression MatchExpression

View file

@ -180,16 +180,39 @@ func (m *FeatureMatcher) match(features *Features) (bool, matchedFeatures, error
matches[dom] = make(domainMatchedFeatures)
}
var isMatch bool
var isMatch = true
var matchedElems []MatchedElement
var err error
if f, ok := features.Flags[featureName]; ok {
isMatch, matchedElems, err = term.MatchExpressions.MatchGetKeys(f.Elements)
if term.MatchExpressions != nil {
isMatch, matchedElems, err = term.MatchExpressions.MatchGetKeys(f.Elements)
}
var meTmp []MatchedElement
if err == nil && isMatch && term.MatchName != nil {
isMatch, meTmp, err = term.MatchName.MatchKeyNames(f.Elements)
matchedElems = append(matchedElems, meTmp...)
}
} else if f, ok := features.Attributes[featureName]; ok {
isMatch, matchedElems, err = term.MatchExpressions.MatchGetValues(f.Elements)
if term.MatchExpressions != nil {
isMatch, matchedElems, err = term.MatchExpressions.MatchGetValues(f.Elements)
}
var meTmp []MatchedElement
if err == nil && isMatch && term.MatchName != nil {
isMatch, meTmp, err = term.MatchName.MatchValueNames(f.Elements)
matchedElems = append(matchedElems, meTmp...)
}
} else if f, ok := features.Instances[featureName]; ok {
matchedElems, err = term.MatchExpressions.MatchGetInstances(f.Elements)
isMatch = len(matchedElems) > 0
if term.MatchExpressions != nil {
matchedElems, err = term.MatchExpressions.MatchGetInstances(f.Elements)
isMatch = len(matchedElems) > 0
}
var meTmp []MatchedElement
if err == nil && isMatch && term.MatchName != nil {
meTmp, err = term.MatchName.MatchInstanceAttributeNames(f.Elements)
isMatch = len(meTmp) > 0
matchedElems = append(matchedElems, meTmp...)
}
} else {
return false, nil, fmt.Errorf("feature %q not available", featureName)
}

View file

@ -31,7 +31,7 @@ func TestRule(t *testing.T) {
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "domain-1.kf-1",
MatchExpressions: MatchExpressionSet{
MatchExpressions: &MatchExpressionSet{
"key-1": newMatchExpression(MatchExists),
},
},
@ -84,7 +84,7 @@ func TestRule(t *testing.T) {
r1.MatchFeatures = FeatureMatcher{
FeatureMatcherTerm{
Feature: "domain-1.kf-1",
MatchExpressions: MatchExpressionSet{},
MatchExpressions: &MatchExpressionSet{},
},
}
m, err = r1.Execute(f)
@ -108,7 +108,7 @@ func TestRule(t *testing.T) {
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "domain-1.vf-1",
MatchExpressions: MatchExpressionSet{
MatchExpressions: &MatchExpressionSet{
"key-1": newMatchExpression(MatchIn, "val-1"),
},
},
@ -129,7 +129,7 @@ func TestRule(t *testing.T) {
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "domain-1.if-1",
MatchExpressions: MatchExpressionSet{
MatchExpressions: &MatchExpressionSet{
"attr-1": newMatchExpression(MatchIn, "val-1"),
},
},
@ -150,13 +150,13 @@ func TestRule(t *testing.T) {
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "domain-1.vf-1",
MatchExpressions: MatchExpressionSet{
MatchExpressions: &MatchExpressionSet{
"key-1": newMatchExpression(MatchIn, "val-x"),
},
},
FeatureMatcherTerm{
Feature: "domain-1.if-1",
MatchExpressions: MatchExpressionSet{
MatchExpressions: &MatchExpressionSet{
"attr-1": newMatchExpression(MatchIn, "val-1"),
},
},
@ -166,7 +166,7 @@ func TestRule(t *testing.T) {
assert.Nilf(t, err, "unexpected error: %v", err)
assert.Nil(t, m.Labels, "instances should not have matched")
r5.MatchFeatures[0].MatchExpressions["key-1"] = newMatchExpression(MatchIn, "val-1")
(*r5.MatchFeatures[0].MatchExpressions)["key-1"] = newMatchExpression(MatchIn, "val-1")
m, err = r5.Execute(f)
assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, r5.Labels, m.Labels, "instances should have matched")
@ -177,7 +177,7 @@ func TestRule(t *testing.T) {
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "domain-1.kf-1",
MatchExpressions: MatchExpressionSet{
MatchExpressions: &MatchExpressionSet{
"key-na": newMatchExpression(MatchExists),
},
},
@ -193,13 +193,13 @@ func TestRule(t *testing.T) {
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "domain-1.kf-1",
MatchExpressions: MatchExpressionSet{
MatchExpressions: &MatchExpressionSet{
"key-1": newMatchExpression(MatchExists),
},
},
},
})
r5.MatchFeatures[0].MatchExpressions["key-1"] = newMatchExpression(MatchIn, "val-1")
(*r5.MatchFeatures[0].MatchExpressions)["key-1"] = newMatchExpression(MatchIn, "val-1")
m, err = r5.Execute(f)
assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, r5.Labels, m.Labels, "instances should have matched")
@ -222,6 +222,7 @@ func TestTemplating(t *testing.T) {
"key-1": "val-1",
"keu-2": "val-2",
"key-3": "val-3",
"key-4": "val-4",
},
},
},
@ -278,7 +279,7 @@ var-2=
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "domain_1.kf_1",
MatchExpressions: MatchExpressionSet{
MatchExpressions: &MatchExpressionSet{
"key-a": newMatchExpression(MatchExists),
"key-c": newMatchExpression(MatchExists),
"foo": newMatchExpression(MatchDoesNotExist),
@ -286,20 +287,20 @@ var-2=
},
FeatureMatcherTerm{
Feature: "domain_1.vf_1",
MatchExpressions: MatchExpressionSet{
MatchExpressions: &MatchExpressionSet{
"key-1": newMatchExpression(MatchIn, "val-1", "val-2"),
"bar": newMatchExpression(MatchDoesNotExist),
},
},
FeatureMatcherTerm{
Feature: "domain_1.if_1",
MatchExpressions: MatchExpressionSet{
MatchExpressions: &MatchExpressionSet{
"attr-1": newMatchExpression(MatchLt, "100"),
},
},
FeatureMatcherTerm{
Feature: "domain_1.if_1",
MatchExpressions: MatchExpressionSet{
MatchExpressions: &MatchExpressionSet{
"attr-1": newMatchExpression(MatchExists),
"attr-2": newMatchExpression(MatchExists),
"attr-3": newMatchExpression(MatchExists),
@ -356,7 +357,7 @@ var-2=
// Use a simple empty matchexpression set to match anything.
FeatureMatcherTerm{
Feature: "domain_1.kf_1",
MatchExpressions: MatchExpressionSet{
MatchExpressions: &MatchExpressionSet{
"key-a": newMatchExpression(MatchExists),
},
},
@ -397,4 +398,42 @@ var-2=
_, err = r2.Execute(f)
assert.Error(t, err)
//
// Test matchName
//
r4 := Rule{
LabelsTemplate: "{{range .domain_1.vf_1}}{{.Name}}={{.Value}}\n{{end}}",
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "domain_1.vf_1",
MatchExpressions: &MatchExpressionSet{
"key-5": newMatchExpression(MatchDoesNotExist),
},
MatchName: newMatchExpression(MatchIn, "key-1", "key-4"),
},
},
}
expectedLabels = map[string]string{
"key-1": "val-1",
"key-4": "val-4",
"key-5": "",
}
m, err = r4.Execute(f)
assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, expectedLabels, m.Labels, "instances should have matched")
r4 = Rule{
Labels: map[string]string{"should-not-match": "true"},
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "domain_1.vf_1",
MatchName: newMatchExpression(MatchIn, "key-not-exists"),
},
},
}
m, err = r4.Execute(f)
assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, map[string]string(nil), m.Labels, "instances should have matched")
}

View file

@ -198,8 +198,16 @@ type FeatureMatcher []FeatureMatcherTerm
// requirements (specified as MatchExpressions) are evaluated against each
// element in the feature set.
type FeatureMatcherTerm struct {
Feature string `json:"feature"`
MatchExpressions MatchExpressionSet `json:"matchExpressions"`
// Feature is the name of the feature set to match against.
Feature string `json:"feature"`
// MatchExpressions is the set of per-element expressions evaluated. These
// match against the value of the specified elements.
// +optional
MatchExpressions *MatchExpressionSet `json:"matchExpressions"`
// MatchName in an expression that is matched against the name of each
// element in the feature set.
// +optional
MatchName *MatchExpression `json:"matchName"`
}
// MatchExpressionSet contains a set of MatchExpressions, each of which is
@ -279,3 +287,7 @@ const (
// output of preceding rules.
RuleBackrefFeature = "matched"
)
// MatchAllNames is a special key in MatchExpressionSet to use field names
// (keys from the input) instead of values when matching.
const MatchAllNames = "*"

View file

@ -58,19 +58,28 @@ func (in *FeatureMatcherTerm) DeepCopyInto(out *FeatureMatcherTerm) {
*out = *in
if in.MatchExpressions != nil {
in, out := &in.MatchExpressions, &out.MatchExpressions
*out = make(MatchExpressionSet, len(*in))
for key, val := range *in {
var outVal *MatchExpression
if val == nil {
(*out)[key] = nil
} else {
in, out := &val, &outVal
*out = new(MatchExpression)
(*in).DeepCopyInto(*out)
*out = new(MatchExpressionSet)
if **in != nil {
in, out := *in, *out
*out = make(map[string]*MatchExpression, len(*in))
for key, val := range *in {
var outVal *MatchExpression
if val == nil {
(*out)[key] = nil
} else {
in, out := &val, &outVal
*out = new(MatchExpression)
(*in).DeepCopyInto(*out)
}
(*out)[key] = outVal
}
(*out)[key] = outVal
}
}
if in.MatchName != nil {
in, out := &in.MatchName, &out.MatchName
*out = new(MatchExpression)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeatureMatcherTerm.

View file

@ -0,0 +1,112 @@
#
# This NodeFeatureRule replicates all built-in cpu feature labels of NFD.
#
apiVersion: nfd.k8s-sigs.io/v1alpha1
kind: NodeFeatureRule
metadata:
name: nfd-builtin-cpu-features
spec:
rules:
- name: "nfd built-in cpu-cpuid labels"
labelsTemplate: |
{{ range .cpu.cpuid }}cpu-cpuid.{{ .Name }}=true
{{ end }}
matchFeatures:
- feature: cpu.cpuid
matchName:
op: NotIn
value:
- "BMI1"
- "BMI2"
- "CLMUL"
- "CMOV"
- "CX16"
- "ERMS"
- "F16C"
- "HTT"
- "LZCNT"
- "MMX"
- "MMXEXT"
- "NX"
- "POPCNT"
- "RDRAND"
- "RDSEED"
- "RDTSCP"
- "SGX"
- "SGXLC"
- "SSE"
- "SSE2"
- "SSE3"
- "SSE4"
- "SSE42"
- "SSSE3"
- name: "nfd built-in cpu-hardware_multithreading label"
labelsTemplate: |
{{ range .cpu.topology }}cpu-{{ .Name }}={{ .Value }}
{{ end }}
matchFeatures:
- feature: cpu.topology
matchName:
op: In
value:
- "hardware_multithreading"
- name: "nfd built-in cpu-cstate and cpu-pstate labels"
labelsTemplate: |
{{ range .cpu.cstate }}cpu-cstate.{{ .Name }}={{ .Value }}
{{ end }}
{{ range .cpu.pstate }}cpu-pstate.{{ .Name }}={{ .Value }}
{{ end }}
matchAny:
- matchFeatures:
- feature: cpu.cstate
matchName:
op: Exists
- matchFeatures:
- feature: cpu.pstate
matchName:
op: Exists
- name: "nfd built-in cpu-model labels"
labelsTemplate: |
{{ range .cpu.model }}cpu-model.{{ .Name }}={{ .Value }}
{{ end }}
matchFeatures:
- feature: cpu.model
matchName:
op: Exists
- name: "nfd built-in cpu-security labels"
labelsTemplate: |
{{ range .cpu.security }}cpu-security.{{ .Name }}={{ .Value }}
{{ end }}
matchFeatures:
- feature: cpu.security
matchName:
op: NotIn
value:
- "tdx.total_keys"
- "sgx.epc"
- "sev.encrypted_state_ids"
- "sev.asids"
- name: "nfd built-in cpu-sst labels"
labelsTemplate: |
{{ range .cpu.sst }}cpu-power.sst_{{ .Name }}={{ .Value }}
{{ end }}
matchFeatures:
- feature: cpu.sst
matchName:
op: Exists
- name: "nfd built-in cpu-coprocessor labels"
labelsTemplate: |
{{ range .cpu.sst }}cpu-coprocessor.{{ .Name }}={{ .Value }}
{{ end }}
matchFeatures:
- feature: cpu.coprocessor
matchName:
op: In
value:
- "nx_gzip"

View file

@ -0,0 +1,29 @@
#
# This NodeFeatureRule replicates all built-in static custom feature labels of NFD.
#
apiVersion: nfd.k8s-sigs.io/v1alpha1
kind: NodeFeatureRule
metadata:
name: nfd-builtin-custom-features
spec:
rules:
- name: "nfd built-in static custom rdma.capable label"
labels:
"custom-rdma.capable": "true"
matchFeatures:
- feature: pci.device
matchExpressions:
vendor:
op: In
value: ["15b3"]
- name: "nfd built-in static custom rdma.available label"
labels:
"custom-rdma.available": "true"
matchFeatures:
- feature: kernel.loadedmodule
matchExpressions:
"ib_uverbs":
op: Exists
"rdma_ucm":
op: Exists

View file

@ -0,0 +1,38 @@
#
# This NodeFeatureRule replicates all built-in kernel feature labels of NFD.
#
apiVersion: nfd.k8s-sigs.io/v1alpha1
kind: NodeFeatureRule
metadata:
name: nfd-builtin-kernel-features
spec:
rules:
- name: "nfd built-in kernel-version labels"
labelsTemplate: |
{{ range .kernel.version }}kernel-version.{{ .Name }}={{ .Value }}
{{ end }}
matchFeatures:
- feature: kernel.version
matchName:
op: Exists
- name: "nfd built-in kernel-config labels"
labelsTemplate: |
{{ range .kernel.config }}kernel-config.{{ .Name }}=true
{{ end }}
matchFeatures:
- feature: kernel.config
matchExpressions:
"NO_HZ": {op: In, value: ["y"]}
"NO_HZ_IDLE": {op: In, value: ["y"]}
"NO_HZ_FULL": {op: In, value: ["y"]}
"PREEMPT": {op: In, value: ["y"]}
- name: "nfd built-in kernel-selinux labels"
labels:
"kernel-selinux.enabled": "true"
matchFeatures:
- feature: kernel.selinux
matchExpressions:
"enabled":
op: IsTrue

View file

@ -0,0 +1,17 @@
#
# This NodeFeatureRule replicates all built-in local feature labels of NFD.
#
apiVersion: nfd.k8s-sigs.io/v1alpha1
kind: NodeFeatureRule
metadata:
name: nfd-builtin-local-features
spec:
rules:
- name: "nfd built-in labels from the local feature source"
labelsTemplate: |
{{ range .local.label }}{{ .Name }}={{ .Value }}
{{ end }}
matchFeatures:
- feature: local.label
matchName:
op: Exists

View file

@ -0,0 +1,34 @@
#
# This NodeFeatureRule replicates all built-in memory feature labels of NFD.
#
apiVersion: nfd.k8s-sigs.io/v1alpha1
kind: NodeFeatureRule
metadata:
name: nfd-builtin-memory-features
spec:
rules:
- name: "nfd built-in memory-numa labels"
labels:
"memory-numa": "true"
matchFeatures:
- feature: memory.numa
matchExpressions:
"is_numa":
op: IsTrue
- name: "nfd built-in memory-nv.present label"
labelsTemplate: "{{ if gt (len .memory.nv ) 0 }}memory-nv.present=true{{ end }}"
matchFeatures:
- feature: memory.nv
matchName:
op: Exists
- name: "nfd built-in memory-nv.dax label"
labels:
"memory.nv.dax": "true"
matchFeatures:
- feature: memory.nv
matchExpressions:
"devtype":
op: In
value: ["nd_dax"]

View file

@ -0,0 +1,28 @@
#
# This NodeFeatureRule replicates all built-in networkfeature labels of NFD.
#
apiVersion: nfd.k8s-sigs.io/v1alpha1
kind: NodeFeatureRule
metadata:
name: nfd-builtin-network-features
spec:
rules:
- name: "nfd built-in network-sriov.capable label"
labels:
"network-sriov.capable": "true"
matchFeatures:
- feature: network.device
matchExpressions:
"sriov_totalvfs":
op: Gt
value: ["0"]
- name: "nfd built-in network-sriov.configured label"
labels:
"network-sriov.configured": "true"
matchFeatures:
- feature: network.device
matchExpressions:
"network-sriov_numvfs":
op: Gt
value: ["0"]

View file

@ -0,0 +1,32 @@
#
# This NodeFeatureRule replicates all built-in pci feature labels of NFD.
#
apiVersion: nfd.k8s-sigs.io/v1alpha1
kind: NodeFeatureRule
metadata:
name: nfd-builtin-pci-features
spec:
rules:
- name: "nfd built-in pci-<device>.present labels"
labelsTemplate: |
{{ range .pci.device }}pci-{{ .class }}_{{ .vendor }}.present=true
{{ end }}
matchFeatures:
- feature: pci.device
matchExpressions:
"class":
op: InRegexp
value: ["^03", "^0b40", "^12"]
- name: "nfd built-in pci-<device>.sriov.capable labels"
labelsTemplate: |
{{ range .pci.device }}pci-{{ .class }}_{{ .vendor }}.sriov.capable=true
{{ end }}
matchFeatures:
- feature: pci.device
matchExpressions:
"class":
op: InRegexp
value: ["^03", "^0b40", "^12"]
"sriov_totalvfs":
op: Exists

View file

@ -0,0 +1,18 @@
#
# This NodeFeatureRule replicates all built-in storage feature labels of NFD.
#
apiVersion: nfd.k8s-sigs.io/v1alpha1
kind: NodeFeatureRule
metadata:
name: nfd-builtin-storage-features
spec:
rules:
- name: "nfd built-in storage-nonrotationaldisk label"
labels:
"storage-nonrotationaldisk": "true"
matchFeatures:
- feature: storage.block
matchExpressions:
"rotational":
op: In
value: ["0"]

View file

@ -0,0 +1,23 @@
#
# This NodeFeatureRule replicates all built-in system feature labels of NFD.
#
apiVersion: nfd.k8s-sigs.io/v1alpha1
kind: NodeFeatureRule
metadata:
name: nfd-builtin-system-features
spec:
rules:
- name: "nfd built-in system-os_release labels"
labelsTemplate: |
{{ range .system.osrelease }}system-os_release.{{ .Name }}={{ .Value }}
{{ end }}
matchFeatures:
- feature: system.osrelease
matchName:
op: In
value:
- "ID"
- "VERSION_ID"
- "VERSION_ID.major"
- "VERSION_ID.minor"

View file

@ -0,0 +1,19 @@
#
# This NodeFeatureRule replicates all built-in usb feature labels of NFD.
#
apiVersion: nfd.k8s-sigs.io/v1alpha1
kind: NodeFeatureRule
metadata:
name: nfd-builtin-usb-features
spec:
rules:
- name: "nfd built-in usb-<device>.present labels"
labelsTemplate: |
{{ range .usb.device }}usb-{{ .class }}_{{ .vendor }}_{{ .device }}.present=true
{{ end }}
matchFeatures:
- feature: usb.device
matchExpressions:
"class":
op: In
value: ["0e", "ef", "fe", "ff"]

View file

@ -31,7 +31,7 @@ func getStaticFeatureConfig() []CustomRule {
MatchFeatures: nfdv1alpha1.FeatureMatcher{
nfdv1alpha1.FeatureMatcherTerm{
Feature: "pci.device",
MatchExpressions: nfdv1alpha1.MatchExpressionSet{
MatchExpressions: &nfdv1alpha1.MatchExpressionSet{
"vendor": &nfdv1alpha1.MatchExpression{
Op: nfdv1alpha1.MatchIn,
Value: nfdv1alpha1.MatchValue{"15b3"}},
@ -47,7 +47,7 @@ func getStaticFeatureConfig() []CustomRule {
MatchFeatures: nfdv1alpha1.FeatureMatcher{
nfdv1alpha1.FeatureMatcherTerm{
Feature: "kernel.loadedmodule",
MatchExpressions: nfdv1alpha1.MatchExpressionSet{
MatchExpressions: &nfdv1alpha1.MatchExpressionSet{
"ib_uverbs": &nfdv1alpha1.MatchExpression{
Op: nfdv1alpha1.MatchExists,
},

View file

@ -17,6 +17,13 @@ spec:
matchExpressions:
"flag_1": {op: Exists}
- name: "e2e-flag-test-2"
labels:
e2e-flag-test-2: "true"
matchFeatures:
- feature: "fake.flag"
matchName: {op: In, value: ["flag_2"]}
# Negative test not supposed to create a label
- name: "e2e-flag-test-neg-1"
labels:
@ -26,6 +33,13 @@ spec:
matchExpressions:
"flag_1": {op: DoesNotExist}
- name: "e2e-flag-test-neg-2"
labels:
e2e-flag-test-neg-2: "true"
matchFeatures:
- feature: "fake.flag"
matchName: {op: In, value: ["flag_x"]}
#
# Simple test rules for attribute features
#
@ -40,6 +54,13 @@ spec:
"attr_1": {op: IsTrue}
"attr_2": {op: IsFalse}
- name: "e2e-attribute-test-2"
labels:
e2e-attribute-test-2: "true"
matchFeatures:
- feature: "fake.attribute"
matchName: {op: In, value: ["attr_2", "attr_x"]}
# Negative test not supposed to create a label
- name: "e2e-attribute-test-neg-1"
labels:
@ -50,8 +71,15 @@ spec:
"attr_1": {op: IsTrue}
"attr_2": {op: IsTrue}
- name: "e2e-attribute-test-neg-2"
labels:
e2e-attribute-test-neg-2: "true"
matchFeatures:
- feature: "fake.attribute"
matchName: {op: In, value: ["attr_x"]}
#
# Simple test rules for instnace features
# Simple test rules for instance features
#
- name: "e2e-instance-test-1"
labels:
@ -65,6 +93,13 @@ spec:
"attr_1": {op: In, value: ["true"]}
"attr_3": {op: Gt, value: ["10"]}
- name: "e2e-instance-test-2"
labels:
e2e-instance-test-2: "true"
matchFeatures:
- feature: "fake.instance"
matchName: {op: In, value: ["attr_1", "attr_x"]}
# Negative test not supposed to create a label
- name: "e2e-instance-test-neg-1"
labels:
@ -74,3 +109,10 @@ spec:
matchExpressions:
"attr_1": {op: In, value: ["true"]}
"attr_3": {op: Lt, value: ["10"]}
- name: "e2e-instance-test-neg-2"
labels:
e2e-instance-test-neg-2: "true"
matchFeatures:
- feature: "fake.instance"
matchName: {op: In, value: ["attr_x"]}

View file

@ -40,3 +40,12 @@ spec:
matchExpressions:
"attr_1": {op: In, value: ["true"]}
- name: "e2e-template-test-2"
labelsTemplate: |
{{ range .fake.attribute }}e2e-template-test-2-{{ .Name }}={{ .Value }}
{{ end }}
matchFeatures:
- feature: "fake.attribute"
matchExpressions:
"attr_2": {op: IsFalse}
matchName: {op: In, value: ["attr_3"]}

View file

@ -20,6 +20,7 @@ import (
"context"
"encoding/json"
"fmt"
"maps"
"path/filepath"
"strings"
"time"
@ -653,7 +654,7 @@ var _ = NFDDescribe(Label("nfd-master"), func() {
// Test NodeFeatureRule
//
Context("and nfd-worker and NodeFeatureRules objects deployed", func() {
Context("and nfd-worker and NodeFeatureRules objects deployed", Label("nodefeaturerule"), func() {
testTolerations := []corev1.Toleration{
{
Key: "feature.node.kubernetes.io/fake-special-node",
@ -714,8 +715,11 @@ core:
expectedLabels := map[string]k8sLabels{
"*": {
nfdv1alpha1.FeatureLabelNs + "/e2e-flag-test-1": "true",
nfdv1alpha1.FeatureLabelNs + "/e2e-flag-test-2": "true",
nfdv1alpha1.FeatureLabelNs + "/e2e-attribute-test-1": "true",
nfdv1alpha1.FeatureLabelNs + "/e2e-attribute-test-2": "true",
nfdv1alpha1.FeatureLabelNs + "/e2e-instance-test-1": "true",
nfdv1alpha1.FeatureLabelNs + "/e2e-instance-test-2": "true",
},
}
@ -729,13 +733,17 @@ core:
Expect(testutils.CreateNodeFeatureRulesFromFile(ctx, nfdClient, "nodefeaturerule-2.yaml")).NotTo(HaveOccurred())
// Add features from NodeFeatureRule #2
expectedLabels["*"][nfdv1alpha1.FeatureLabelNs+"/e2e-matchany-test-1"] = "true"
expectedLabels["*"][nfdv1alpha1.FeatureLabelNs+"/e2e-template-test-1-instance_1"] = "found"
expectedLabels["*"][nfdv1alpha1.FeatureLabelNs+"/e2e-template-test-1-instance_2"] = "found"
expectedLabels["*"][nfdv1alpha1.FeatureLabelNs+"/dynamic-label"] = "true"
maps.Copy(expectedLabels["*"], k8sLabels{
nfdv1alpha1.FeatureLabelNs + "/e2e-matchany-test-1": "true",
nfdv1alpha1.FeatureLabelNs + "/e2e-template-test-1-instance_1": "found",
nfdv1alpha1.FeatureLabelNs + "/e2e-template-test-1-instance_2": "found",
nfdv1alpha1.FeatureLabelNs + "/e2e-template-test-2-attr_2": "false",
nfdv1alpha1.FeatureLabelNs + "/e2e-template-test-2-attr_3": "10",
nfdv1alpha1.FeatureLabelNs + "/dynamic-label": "true",
})
expectedAnnotations := map[string]k8sAnnotations{
"*": {
"nfd.node.kubernetes.io/feature-labels": "dynamic-label,e2e-attribute-test-1,e2e-flag-test-1,e2e-instance-test-1,e2e-matchany-test-1,e2e-template-test-1-instance_1,e2e-template-test-1-instance_2"},
"nfd.node.kubernetes.io/feature-labels": "dynamic-label,e2e-attribute-test-1,e2e-attribute-test-2,e2e-flag-test-1,e2e-flag-test-2,e2e-instance-test-1,e2e-instance-test-2,e2e-matchany-test-1,e2e-template-test-1-instance_1,e2e-template-test-1-instance_2,e2e-template-test-2-attr_2,e2e-template-test-2-attr_3"},
}
By("Verifying node labels from NodeFeatureRules #1 and #2")