From 8f4d42f887cf5f4f917f4cb2df0294fdd988ef72 Mon Sep 17 00:00:00 2001 From: Sebastian Sch Date: Thu, 28 Oct 2021 16:46:58 +0300 Subject: [PATCH] Fix webhook for virtual workers this commit fix the validation webhook to support virtual workers using PCI passthrough Signed-off-by: Sebastian Sch --- api/v1/helper.go | 10 +++ cmd/sriov-network-config-daemon/start.go | 7 +-- pkg/utils/utils_virtual.go | 7 +++ pkg/webhook/validate.go | 17 +++-- pkg/webhook/validate_test.go | 80 ++++++++++++++++++++++-- 5 files changed, 106 insertions(+), 15 deletions(-) diff --git a/api/v1/helper.go b/api/v1/helper.go index 650d17643..c17a85718 100644 --- a/api/v1/helper.go +++ b/api/v1/helper.go @@ -106,6 +106,16 @@ func IsSupportedModel(vendorId, deviceId string) bool { return false } +func IsVfSupportedModel(vendorId, deviceId string) bool { + for _, n := range NicIdMap { + ids := strings.Split(n, " ") + if vendorId == ids[0] && deviceId == ids[2] { + return true + } + } + return false +} + func IsEnabledUnsupportedVendor(vendorId string, unsupportedNicIdMap map[string]string) bool { for _, n := range unsupportedNicIdMap { if IsValidPciString(n) { diff --git a/cmd/sriov-network-config-daemon/start.go b/cmd/sriov-network-config-daemon/start.go index 92742e560..48c60ba0f 100644 --- a/cmd/sriov-network-config-daemon/start.go +++ b/cmd/sriov-network-config-daemon/start.go @@ -40,11 +40,6 @@ var ( kubeconfig string nodeName string } - - // PlatformMap contains supported platforms for virtual VF - platformMap = map[string]utils.PlatformType{ - "openstack": utils.VirtualOpenStack, - } ) func init() { @@ -147,7 +142,7 @@ func runStartCmd(cmd *cobra.Command, args []string) { nodeInfo, err := kubeclient.CoreV1().Nodes().Get(context.Background(), startOpts.nodeName, v1.GetOptions{}) if err == nil { - for key, pType := range platformMap { + for key, pType := range utils.PlatformMap { if strings.Contains(strings.ToLower(nodeInfo.Spec.ProviderID), strings.ToLower(key)) { platformType = pType } diff --git a/pkg/utils/utils_virtual.go b/pkg/utils/utils_virtual.go index d11054978..ba0d2d97a 100644 --- a/pkg/utils/utils_virtual.go +++ b/pkg/utils/utils_virtual.go @@ -34,6 +34,13 @@ func (e PlatformType) String() string { } } +var ( + // PlatformMap contains supported platforms for virtual VF + PlatformMap = map[string]PlatformType{ + "openstack": VirtualOpenStack, + } +) + const ( ospMetaDataDir = "/host/var/config/openstack/latest/" ospNetworkData = ospMetaDataDir + "/network_data.json" diff --git a/pkg/webhook/validate.go b/pkg/webhook/validate.go index af7ef3a52..2c19ed0bf 100644 --- a/pkg/webhook/validate.go +++ b/pkg/webhook/validate.go @@ -3,6 +3,7 @@ package webhook import ( "context" "fmt" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils" "os" "regexp" "strconv" @@ -10,6 +11,7 @@ import ( "github.com/golang/glog" "k8s.io/api/admission/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -169,7 +171,7 @@ func dynamicValidateSriovNetworkNodePolicy(cr *sriovnetworkv1.SriovNetworkNodePo nodesSelected = true for _, ns := range nsList.Items { if ns.GetName() == node.GetName() { - if ok, err := validatePolicyForNodeState(cr, &ns); err != nil || !ok { + if ok, err := validatePolicyForNodeState(cr, &ns, &node); err != nil || !ok { return false, err } } @@ -195,10 +197,10 @@ func dynamicValidateSriovNetworkNodePolicy(cr *sriovnetworkv1.SriovNetworkNodePo return true, nil } -func validatePolicyForNodeState(policy *sriovnetworkv1.SriovNetworkNodePolicy, state *sriovnetworkv1.SriovNetworkNodeState) (bool, error) { +func validatePolicyForNodeState(policy *sriovnetworkv1.SriovNetworkNodePolicy, state *sriovnetworkv1.SriovNetworkNodeState, node *corev1.Node) (bool, error) { glog.V(2).Infof("validatePolicyForNodeState(): validate policy %s for node %s.", policy.GetName(), state.GetName()) for _, iface := range state.Status.Interfaces { - if validateNicModel(&policy.Spec.NicSelector, &iface, state.GetName()) { + if validateNicModel(&policy.Spec.NicSelector, &iface, node) { interfaceSelected = true if policy.GetName() != "default" && policy.Spec.NumVfs == 0 { return false, fmt.Errorf("numVfs(%d) in CR %s is not allowed", policy.Spec.NumVfs, policy.GetName()) @@ -256,7 +258,7 @@ func keys(m map[string]([]string)) []string { return keys } -func validateNicModel(selector *sriovnetworkv1.SriovNetworkNicSelector, iface *sriovnetworkv1.InterfaceExt, nodeName string) bool { +func validateNicModel(selector *sriovnetworkv1.SriovNetworkNicSelector, iface *sriovnetworkv1.InterfaceExt, node *corev1.Node) bool { if selector.Vendor != "" && selector.Vendor != iface.Vendor { return false } @@ -285,5 +287,12 @@ func validateNicModel(selector *sriovnetworkv1.SriovNetworkNicSelector, iface *s if sriovnetworkv1.IsSupportedModel(iface.Vendor, iface.DeviceID) { return true } + + for key := range utils.PlatformMap { + if strings.Contains(strings.ToLower(node.Spec.ProviderID), strings.ToLower(key)) && sriovnetworkv1.IsVfSupportedModel(iface.Vendor, iface.DeviceID) { + return true + } + } + return false } diff --git a/pkg/webhook/validate_test.go b/pkg/webhook/validate_test.go index 96cc7ba27..704d55737 100644 --- a/pkg/webhook/validate_test.go +++ b/pkg/webhook/validate_test.go @@ -2,6 +2,7 @@ package webhook import ( "fmt" + corev1 "k8s.io/api/core/v1" "os" "testing" @@ -121,6 +122,10 @@ func newNodePolicy() *SriovNetworkNodePolicy { } } +func NewNode() *corev1.Node { + return &corev1.Node{Spec: corev1.NodeSpec{ProviderID: "openstack"}} +} + func TestValidateSriovOperatorConfigWithDefaultOperatorConfig(t *testing.T) { var err error var ok bool @@ -205,7 +210,7 @@ func TestValidatePolicyForNodeStateWithValidPolicy(t *testing.T) { }, } g := NewGomegaWithT(t) - ok, err := validatePolicyForNodeState(policy, state) + ok, err := validatePolicyForNodeState(policy, state, NewNode()) g.Expect(err).NotTo(HaveOccurred()) g.Expect(ok).To(Equal(true)) } @@ -232,7 +237,7 @@ func TestValidatePolicyForNodeStateWithInvalidNumVfsPolicy(t *testing.T) { }, } g := NewGomegaWithT(t) - ok, err := validatePolicyForNodeState(policy, state) + ok, err := validatePolicyForNodeState(policy, state, NewNode()) g.Expect(err).To(MatchError(ContainSubstring(fmt.Sprintf("numVfs(%d) in CR %s exceed the maximum allowed value(%d)", policy.Spec.NumVfs, policy.GetName(), state.Status.Interfaces[0].TotalVfs)))) g.Expect(ok).To(Equal(false)) } @@ -423,7 +428,7 @@ func TestValidatePolicyForNodeStateWithInvalidDevice(t *testing.T) { g.Expect(err).ToNot(HaveOccurred()) g.Expect(cfg).ToNot(BeNil()) kubeclient = kubernetes.NewForConfigOrDie(cfg) - ok, err := validatePolicyForNodeState(policy, state) + ok, err := validatePolicyForNodeState(policy, state, NewNode()) g.Expect(err).NotTo(HaveOccurred()) g.Expect(ok).To(Equal(true)) } @@ -446,7 +451,7 @@ func TestValidatePolicyForNodeStateWithInvalidPfName(t *testing.T) { }, } g := NewGomegaWithT(t) - ok, err := validatePolicyForNodeState(policy, state) + ok, err := validatePolicyForNodeState(policy, state, NewNode()) g.Expect(err).NotTo(HaveOccurred()) g.Expect(ok).To(Equal(true)) g.Expect(interfaceSelected).To(Equal(false)) @@ -470,7 +475,7 @@ func TestValidatePolicyForNodeStateWithValidPfName(t *testing.T) { }, } g := NewGomegaWithT(t) - ok, err := validatePolicyForNodeState(policy, state) + ok, err := validatePolicyForNodeState(policy, state, NewNode()) g.Expect(err).NotTo(HaveOccurred()) g.Expect(ok).To(Equal(true)) g.Expect(interfaceSelected).To(Equal(true)) @@ -492,3 +497,68 @@ func TestStaticValidateSriovNetworkNodePolicyWithInvalidNicSelector(t *testing.T g.Expect(err).To(HaveOccurred()) g.Expect(ok).To(Equal(false)) } + +func TestValidatePolicyForNodeStateWithValidVFAndNetFilter(t *testing.T) { + interfaceSelected = false + state := &SriovNetworkNodeState{ + Spec: SriovNetworkNodeStateSpec{ + Interfaces: []Interface{ + { + Name: "ens803f1", + NumVfs: 1, + PciAddress: "0000:86:00.1", + VfGroups: []VfGroup{ + { + DeviceType: "netdevice", + ResourceName: "nic1", + }, + }, + }, + }, + }, + Status: SriovNetworkNodeStateStatus{ + Interfaces: []InterfaceExt{ + { + VFs: []VirtualFunction{ + { + DeviceID: "154c", + Driver: "iavf", + PciAddress: "0000:86:00.1", + Mtu: 1500, + VfID: 0, + }, + }, + DeviceID: "154c", + Driver: "iavf", + Mtu: 1500, + Name: "ens803f0", + PciAddress: "0000:86:00.0", + Vendor: "8086", + NumVfs: 1, + TotalVfs: 64, + NetFilter: "openstack/NetworkID:e48c7670-bcb4-4f9c-8038-012b6571501d", + }, + }, + }, + } + policy := &SriovNetworkNodePolicy{ + Spec: SriovNetworkNodePolicySpec{ + DeviceType: "netdevice", + NicSelector: SriovNetworkNicSelector{ + PfNames: []string{"ens803f0"}, + NetFilter: "openstack/NetworkID:e48c7670-bcb4-4f9c-8038-012b6571501d", + }, + NodeSelector: map[string]string{ + "feature.node.kubernetes.io/network-sriov.capable": "true", + }, + NumVfs: 1, + Priority: 99, + ResourceName: "p0", + }, + } + g := NewGomegaWithT(t) + ok, err := validatePolicyForNodeState(policy, state, NewNode()) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(ok).To(Equal(true)) + g.Expect(interfaceSelected).To(Equal(true)) +}