diff --git a/docs/usage/customization-guide.md b/docs/usage/customization-guide.md index a721ecd3a..c5d7676fc 100644 --- a/docs/usage/customization-guide.md +++ b/docs/usage/customization-guide.md @@ -1062,278 +1062,3 @@ must be present): vendor: "0fff" device: "abcd" ``` - -## Legacy custom rule syntax - -**DEPRECATED**: use the new rule syntax instead. - -The `custom` source supports the legacy `matchOn` rule syntax for -backwards-compatibility. - -To aid in making the legacy rule syntax clearer, we define a general and a per -rule nomenclature, keeping things as consistent as possible. - -### General nomenclature and definitions - -```plaintext -Rule :Represents a matching logic that is used to match on a feature. -Rule Input :The input a Rule is provided. This determines how a Rule performs the match operation. -Matcher :A composition of Rules, each Matcher may be composed of at most one instance of each Rule. -``` - -### Custom features format (using the nomenclature defined above) - -Rules are specified under `sources.custom` in the nfd-worker configuration -file. - -```yaml -sources: - custom: - - name: - value: - matchOn: - - : - [: ] - - - - ... - - ... - - - - - - ... - - ... - - -``` - -The label is constructed by adding `custom-` prefix to the name field, label -value defaults to `true` if not specified in the rule spec: - -```plaintext -feature.node.kubernetes.io/custom- = -``` - -### Matching process - -Specifying Rules to match on a feature is done by providing a list of Matchers. -Each Matcher contains one or more Rules. - -Logical _OR_ is performed between Matchers and logical _AND_ is performed -between Rules of a given Matcher. - -### Rules - -#### pciid rule - -##### Nomenclature - -```plaintext -Attribute :A PCI attribute. -Element :An identifier of the PCI attribute. -``` - -The PciId Rule allows matching the PCI devices in the system on the following -Attributes: `class`,`vendor` and `device`. A list of Elements is provided for -each Attribute. - -##### Format - -```yaml -pciId : - class: [, ...] - vendor: [, ...] - device: [, ...] -``` - -Matching is done by performing a logical _OR_ between Elements of an Attribute -and logical _AND_ between the specified Attributes for each PCI device in the -system. At least one Attribute must be specified. Missing attributes will not -partake in the matching process. - -#### UsbId rule - -##### Nomenclature - -```plaintext -Attribute :A USB attribute. -Element :An identifier of the USB attribute. -``` - -The UsbId Rule allows matching the USB devices in the system on the following -Attributes: `class`,`vendor`, `device` and `serial`. A list of Elements is -provided for each Attribute. - -##### Format - -```yaml -usbId : - class: [, ...] - vendor: [, ...] - device: [, ...] - serial: [, ...] -``` - -Matching is done by performing a logical _OR_ between Elements of an Attribute -and logical _AND_ between the specified Attributes for each USB device in the -system. At least one Attribute must be specified. Missing attributes will not -partake in the matching process. - -#### LoadedKMod rule - -##### Nomenclature - -```plaintext -Element :A kernel module -``` - -The LoadedKMod Rule allows matching the loaded kernel modules in the system -against a provided list of Elements. - -##### Format - -```yaml -loadedKMod : [, ...] -``` - -Matching is done by performing logical _AND_ for each provided Element, i.e -the Rule will match if all provided Elements (kernel modules) are loaded in the -system. - -#### CpuId rule - -##### Nomenclature - -```plaintext -Element :A CPUID flag -``` - -The Rule allows matching the available CPUID flags in the system against a -provided list of Elements. - -##### Format - -```yaml -cpuId : [, ...] -``` - -Matching is done by performing logical _AND_ for each provided Element, i.e the -Rule will match if all provided Elements (CPUID flag strings) are available in -the system. - -#### Kconfig rule - -##### Nomenclature - -```plaintext -Element :A Kconfig option -``` - -The Rule allows matching the kconfig options in the system against a provided -list of Elements. - -##### Format - -```yaml -kConfig: ['>, ...] -``` - -Matching is done by performing logical _AND_ for each provided Element, i.e the -Rule will match if all provided Elements (kernel config options) are enabled -(`y` or `m`) or matching `=` in the kernel. - -#### Nodename rule - -##### Nomenclature - -```plaintext -Element :A nodename regexp pattern -``` - -The Rule allows matching the node's name against a provided list of Elements. - -##### Format - -```yaml -nodename: [ , ... ] -``` - -Matching is done by performing logical _OR_ for each provided Element, i.e the -Rule will match if one of the provided Elements (nodename regexp pattern) -matches the node's name. - -### Legacy custom rule example - -```yaml -custom: - - name: "my.kernel.feature" - matchOn: - - loadedKMod: ["kmod1", "kmod2"] - - name: "my.pci.feature" - matchOn: - - pciId: - vendor: ["15b3"] - device: ["1014", "1017"] - - name: "my.usb.feature" - matchOn: - - usbId: - vendor: ["1d6b"] - device: ["0003"] - serial: ["090129a"] - - name: "my.combined.feature" - matchOn: - - loadedKMod : ["vendor_kmod1", "vendor_kmod2"] - pciId: - vendor: ["15b3"] - device: ["1014", "1017"] - - name: "vendor.feature.node.kubernetes.io/accumulated.feature" - matchOn: - - loadedKMod : ["some_kmod1", "some_kmod2"] - - pciId: - vendor: ["15b3"] - device: ["1014", "1017"] - - name: "my.kernel.featureneedscpu" - matchOn: - - kConfig: ["KVM_INTEL"] - - cpuId: ["VMX"] - - name: "my.kernel.modulecompiler" - matchOn: - - kConfig: ["GCC_VERSION=100101"] - loadedKMod: ["kmod1"] - - name: "profile.node.kubernetes.io/my-datacenter" - value: "datacenter-1" - matchOn: - - nodename: [ "node-datacenter1-rack.*-server.*" ] -``` - -__In the example above:__ - -- A node would contain the label: - `feature.node.kubernetes.io/custom-my.kernel.feature=true` if the node has - `kmod1` _AND_ `kmod2` kernel modules loaded. -- A node would contain the label: - `feature.node.kubernetes.io/custom-my.pci.feature=true` if the node contains - a PCI device with a PCI vendor ID of `15b3` _AND_ PCI device ID of `1014` _OR_ - `1017`. -- A node would contain the label: - `feature.node.kubernetes.io/custom-my.usb.feature=true` if the node contains - a USB device with a USB vendor ID of `1d6b` _AND_ USB device ID of `0003`. -- A node would contain the label: - `feature.node.kubernetes.io/custom-my.combined.feature=true` if - `vendor_kmod1` _AND_ `vendor_kmod2` kernel modules are loaded __AND__ the node - contains a PCI device - with a PCI vendor ID of `15b3` _AND_ PCI device ID of `1014` _or_ `1017`. -- A node would contain the label: - `vendor.feature.node.kubernetes.io/accumulated.feature=true` if - `some_kmod1` _AND_ `some_kmod2` kernel modules are loaded __OR__ the node - contains a PCI device - with a PCI vendor ID of `15b3` _AND_ PCI device ID of `1014` _OR_ `1017`. -- A node would contain the label: - `feature.node.kubernetes.io/custom-my.kernel.featureneedscpu=true` if - `KVM_INTEL` kernel config is enabled __AND__ the node CPU supports `VMX` - virtual machine extensions -- A node would contain the label: - `feature.node.kubernetes.io/custom-my.kernel.modulecompiler=true` if the - in-tree `kmod1` kernel module is loaded __AND__ it's built with - `GCC_VERSION=100101`. -- A node would contain the label: - `profile.node.kubernetes.io/my-datacenter=datacenter-1` if the node's name - matches the `node-datacenter1-rack.*-server.*` pattern, e.g. - `node-datacenter1-rack2-server42` diff --git a/source/custom/custom.go b/source/custom/custom.go index 769e7b440..f3782e372 100644 --- a/source/custom/custom.go +++ b/source/custom/custom.go @@ -17,50 +17,24 @@ limitations under the License. package custom import ( - "encoding/json" "fmt" - "reflect" - "strings" "k8s.io/klog/v2" - "sigs.k8s.io/yaml" nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1" "sigs.k8s.io/node-feature-discovery/pkg/utils" "sigs.k8s.io/node-feature-discovery/source" - "sigs.k8s.io/node-feature-discovery/source/custom/rules" ) // Name of this feature source const Name = "custom" -// LegacyMatcher contains the legacy custom rules. -type LegacyMatcher struct { - PciID *rules.PciIDRule `json:"pciId,omitempty"` - UsbID *rules.UsbIDRule `json:"usbId,omitempty"` - LoadedKMod *rules.LoadedKModRule `json:"loadedKMod,omitempty"` - CpuID *rules.CpuIDRule `json:"cpuId,omitempty"` - Kconfig *rules.KconfigRule `json:"kConfig,omitempty"` - Nodename *rules.NodenameRule `json:"nodename,omitempty"` -} - -type LegacyRule struct { - Name string `json:"name"` - Value *string `json:"value,omitempty"` - MatchOn []LegacyMatcher `json:"matchOn"` -} - -type Rule struct { +type CustomRule struct { nfdv1alpha1.Rule } type config []CustomRule -type CustomRule struct { - *LegacyRule - *Rule -} - // newDefaultConfig returns a new config with pre-populated defaults func newDefaultConfig() *config { return &config{} @@ -71,10 +45,6 @@ type customSource struct { config *config } -type legacyRule interface { - Match() (bool, error) -} - // Singleton source instance var ( src = customSource{config: newDefaultConfig()} @@ -115,7 +85,7 @@ func (s *customSource) GetLabels() (source.FeatureLabels, error) { klog.V(2).InfoS("resolving custom features", "configuration", utils.DelayedDumper(allFeatureConfig)) // Iterate over features for _, rule := range allFeatureConfig { - ruleOut, err := rule.execute(features) + ruleOut, err := rule.Execute(features) if err != nil { klog.ErrorS(err, "failed to execute rule") continue @@ -132,112 +102,6 @@ func (s *customSource) GetLabels() (source.FeatureLabels, error) { return labels, nil } -func (r *CustomRule) execute(features *nfdv1alpha1.Features) (nfdv1alpha1.RuleOutput, error) { - if r.LegacyRule != nil { - klog.InfoS("legacy 'matchOn' rule format is deprecated, please convert to the new 'matchFeatures' rule format", "ruleName", r.LegacyRule.Name) - ruleOut, err := r.LegacyRule.execute() - if err != nil { - return nfdv1alpha1.RuleOutput{}, fmt.Errorf("failed to execute legacy rule %s: %w", r.LegacyRule.Name, err) - } - return nfdv1alpha1.RuleOutput{Labels: ruleOut}, nil - } - - if r.Rule != nil { - ruleOut, err := r.Rule.Execute(features) - if err != nil { - return ruleOut, fmt.Errorf("failed to execute rule %s: %w", r.Rule.Name, err) - } - return ruleOut, nil - } - - return nfdv1alpha1.RuleOutput{}, fmt.Errorf("BUG: an empty rule, this really should not happen") -} - -func (r *LegacyRule) execute() (map[string]string, error) { - if len(r.MatchOn) > 0 { - // Logical OR over the legacy rules - matched := false - for _, matcher := range r.MatchOn { - if m, err := matcher.match(); err != nil { - return nil, err - } else if m { - matched = true - break - } - } - if !matched { - return nil, nil - } - } - - // Prefix non-namespaced labels with "custom-" - name := r.Name - if !strings.Contains(name, "/") { - name = "custom-" + name - } - - value := "true" - if r.Value != nil { - value = *r.Value - } - - return map[string]string{name: value}, nil -} - -func (m *LegacyMatcher) match() (bool, error) { - allRules := []legacyRule{ - m.PciID, - m.UsbID, - m.LoadedKMod, - m.CpuID, - m.Kconfig, - m.Nodename, - } - - // return true, nil if all rules match - matchRules := func(rules []legacyRule) (bool, error) { - for _, rule := range rules { - if reflect.ValueOf(rule).IsNil() { - continue - } - if match, err := rule.Match(); err != nil { - return false, err - } else if !match { - return false, nil - } - } - return true, nil - } - - return matchRules(allRules) -} - -// UnmarshalJSON implements the Unmarshaler interface from "encoding/json" -func (c *CustomRule) UnmarshalJSON(data []byte) error { - // Do a raw parse to determine if this is a legacy rule - raw := map[string]json.RawMessage{} - err := yaml.Unmarshal(data, &raw) - if err != nil { - return err - } - - for k := range raw { - if strings.ToLower(k) == "matchon" { - return yaml.Unmarshal(data, &c.LegacyRule) - } - } - - return yaml.Unmarshal(data, &c.Rule) -} - -// MarshalJSON implements the Marshaler interface from "encoding/json" -func (c *CustomRule) MarshalJSON() ([]byte, error) { - if c.LegacyRule != nil { - return json.Marshal(c.LegacyRule) - } - return json.Marshal(c.Rule) -} - func init() { source.Register(&src) } diff --git a/source/custom/rules/cpuid_rule.go b/source/custom/rules/cpuid_rule.go deleted file mode 100644 index 85efebd2c..000000000 --- a/source/custom/rules/cpuid_rule.go +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2020-2021 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 rules - -import ( - "fmt" - - nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1" - "sigs.k8s.io/node-feature-discovery/source" - "sigs.k8s.io/node-feature-discovery/source/cpu" -) - -// CpuIDRule implements Rule for the custom source -type CpuIDRule struct { - nfdv1alpha1.MatchExpressionSet -} - -// Match checks if CpuId rule matches. -func (r *CpuIDRule) Match() (bool, error) { - flags, ok := source.GetFeatureSource("cpu").GetFeatures().Flags[cpu.CpuidFeature] - if !ok { - return false, fmt.Errorf("cpuid information not available") - } - return r.MatchKeys(flags.Elements) -} diff --git a/source/custom/rules/kconfig_rule.go b/source/custom/rules/kconfig_rule.go deleted file mode 100644 index 2461c1907..000000000 --- a/source/custom/rules/kconfig_rule.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2020-2021 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 rules - -import ( - "fmt" - - nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1" - "sigs.k8s.io/node-feature-discovery/source/kernel" -) - -// KconfigRule implements Rule for the custom source -type KconfigRule struct { - nfdv1alpha1.MatchExpressionSet -} - -// Match compares the values of Kernel config provided and legacy config -func (r *KconfigRule) Match() (bool, error) { - options := kernel.GetLegacyKconfig() - if options == nil { - return false, fmt.Errorf("kernel config options not available") - } - return r.MatchValues(options) -} diff --git a/source/custom/rules/loaded_kmod_rule.go b/source/custom/rules/loaded_kmod_rule.go deleted file mode 100644 index 7558f4645..000000000 --- a/source/custom/rules/loaded_kmod_rule.go +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright 2020-2021 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 rules - -import ( - "fmt" - - nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1" - "sigs.k8s.io/node-feature-discovery/source" - "sigs.k8s.io/node-feature-discovery/source/kernel" -) - -// LoadedKModRule matches loaded kernel modules in the system -type LoadedKModRule struct { - nfdv1alpha1.MatchExpressionSet -} - -// Match loaded kernel modules on provided list of kernel modules -func (r *LoadedKModRule) Match() (bool, error) { - modules, ok := source.GetFeatureSource("kernel").GetFeatures().Flags[kernel.LoadedModuleFeature] - if !ok { - return false, fmt.Errorf("info about loaded modules not available") - } - - return r.MatchKeys(modules.Elements) -} diff --git a/source/custom/rules/nodename_rule.go b/source/custom/rules/nodename_rule.go deleted file mode 100644 index 964c9206e..000000000 --- a/source/custom/rules/nodename_rule.go +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright 2021 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 rules - -import ( - "encoding/json" - "fmt" - - nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1" - "sigs.k8s.io/node-feature-discovery/source" - "sigs.k8s.io/node-feature-discovery/source/system" -) - -// NodenameRule matches on nodenames configured in a ConfigMap -type NodenameRule struct { - nfdv1alpha1.MatchExpression -} - -// Match checks if node name matches the rule. -func (r *NodenameRule) Match() (bool, error) { - nodeName, ok := source.GetFeatureSource("system").GetFeatures().Attributes[system.NameFeature].Elements["nodename"] - if !ok || nodeName == "" { - return false, fmt.Errorf("node name not available") - } - return r.MatchExpression.Match(true, nodeName) -} - -// UnmarshalJSON unmarshals and validates the data provided -func (r *NodenameRule) UnmarshalJSON(data []byte) error { - if err := json.Unmarshal(data, &r.MatchExpression); err != nil { - return err - } - // Force regexp matching - if r.Op == nfdv1alpha1.MatchIn { - r.Op = nfdv1alpha1.MatchInRegexp - } - // We need to run Validate() because operator forcing above - return r.Validate() -} diff --git a/source/custom/rules/pci_id_rule.go b/source/custom/rules/pci_id_rule.go deleted file mode 100644 index 9efa4e810..000000000 --- a/source/custom/rules/pci_id_rule.go +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2020-2021 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 rules - -import ( - "fmt" - - nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1" - "sigs.k8s.io/node-feature-discovery/source" - "sigs.k8s.io/node-feature-discovery/source/pci" -) - -type PciIDRule struct { - nfdv1alpha1.MatchExpressionSet -} - -// Match PCI devices on provided PCI device attributes -func (r *PciIDRule) Match() (bool, error) { - devs, ok := source.GetFeatureSource("pci").GetFeatures().Instances[pci.DeviceFeature] - if !ok { - return false, fmt.Errorf("cpuid information not available") - } - - return r.MatchInstances(devs.Elements) -} diff --git a/source/custom/rules/usb_id_rule.go b/source/custom/rules/usb_id_rule.go deleted file mode 100644 index d0c83c106..000000000 --- a/source/custom/rules/usb_id_rule.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2020 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 rules - -import ( - "fmt" - - nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1" - "sigs.k8s.io/node-feature-discovery/source" - "sigs.k8s.io/node-feature-discovery/source/usb" -) - -type UsbIDRule struct { - nfdv1alpha1.MatchExpressionSet -} - -// Match USB devices on provided USB device attributes -func (r *UsbIDRule) Match() (bool, error) { - devs, ok := source.GetFeatureSource("usb").GetFeatures().Instances[usb.DeviceFeature] - if !ok { - return false, fmt.Errorf("usb device information not available") - } - return r.MatchInstances(devs.Elements) -} diff --git a/source/custom/static_features.go b/source/custom/static_features.go index 3f44df99e..c89851913 100644 --- a/source/custom/static_features.go +++ b/source/custom/static_features.go @@ -25,33 +25,29 @@ import ( func getStaticFeatureConfig() []CustomRule { return []CustomRule{ { - Rule: &Rule{ - nfdv1alpha1.Rule{ - Name: "RDMA capable static rule", - Labels: map[string]string{"rdma.capable": "true"}, - MatchFeatures: nfdv1alpha1.FeatureMatcher{ - nfdv1alpha1.FeatureMatcherTerm{ - Feature: "pci.device", - MatchExpressions: nfdv1alpha1.MatchExpressionSet{ - "vendor": nfdv1alpha1.MustCreateMatchExpression(nfdv1alpha1.MatchIn, "15b3"), - }, + nfdv1alpha1.Rule{ + Name: "RDMA capable static rule", + Labels: map[string]string{"rdma.capable": "true"}, + MatchFeatures: nfdv1alpha1.FeatureMatcher{ + nfdv1alpha1.FeatureMatcherTerm{ + Feature: "pci.device", + MatchExpressions: nfdv1alpha1.MatchExpressionSet{ + "vendor": nfdv1alpha1.MustCreateMatchExpression(nfdv1alpha1.MatchIn, "15b3"), }, }, }, }, }, { - Rule: &Rule{ - nfdv1alpha1.Rule{ - Name: "RDMA available static rule", - Labels: map[string]string{"rdma.available": "true"}, - MatchFeatures: nfdv1alpha1.FeatureMatcher{ - nfdv1alpha1.FeatureMatcherTerm{ - Feature: "kernel.loadedmodule", - MatchExpressions: nfdv1alpha1.MatchExpressionSet{ - "ib_uverbs": nfdv1alpha1.MustCreateMatchExpression(nfdv1alpha1.MatchExists), - "rdma_ucm": nfdv1alpha1.MustCreateMatchExpression(nfdv1alpha1.MatchExists), - }, + nfdv1alpha1.Rule{ + Name: "RDMA available static rule", + Labels: map[string]string{"rdma.available": "true"}, + MatchFeatures: nfdv1alpha1.FeatureMatcher{ + nfdv1alpha1.FeatureMatcherTerm{ + Feature: "kernel.loadedmodule", + MatchExpressions: nfdv1alpha1.MatchExpressionSet{ + "ib_uverbs": nfdv1alpha1.MustCreateMatchExpression(nfdv1alpha1.MatchExists), + "rdma_ucm": nfdv1alpha1.MustCreateMatchExpression(nfdv1alpha1.MatchExists), }, }, },