Skip to content

Commit

Permalink
Merge pull request kubevirt#11618 from AlonaKaplan/deviceinfo_downwar…
Browse files Browse the repository at this point in the history
…dapi

Deviceinfo downwardAPI
  • Loading branch information
kubevirt-bot authored Apr 18, 2024
2 parents e87c122 + c9a80b5 commit 895075a
Show file tree
Hide file tree
Showing 41 changed files with 926 additions and 222 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ lint:
pkg/network/admitter/... \
pkg/network/namescheme/... \
pkg/network/domainspec/... \
pkg/network/sriov/... \
pkg/network/deviceinfo/... \
tests/console/... \
tests/libnet/... \
tests/libnode/... \
Expand Down
4 changes: 4 additions & 0 deletions api/openapi-spec/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -17581,6 +17581,10 @@
"description": "DomainAttachmentType is a standard domain network attachment method kubevirt supports. Supported values: \"tap\". The standard domain attachment can be used instead or in addition to the sidecarImage. version: 1alphav1",
"type": "string"
},
"downwardAPI": {
"description": "DownwardAPI specifies what kind of data should be exposed to the binding plugin sidecar. Supported values: \"device-info\" version: v1alphav1",
"type": "string"
},
"migration": {
"description": "Migration means the VM using the plugin can be safely migrated version: 1alphav1",
"$ref": "#/definitions/v1.InterfaceBindingMigration"
Expand Down
10 changes: 10 additions & 0 deletions manifests/generated/kv-resource.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,11 @@ spec:
be used instead or in addition to the sidecarImage.
version: 1alphav1'
type: string
downwardAPI:
description: 'DownwardAPI specifies what kind of data
should be exposed to the binding plugin sidecar. Supported
values: "device-info" version: v1alphav1'
type: string
migration:
description: 'Migration means the VM using the plugin
can be safely migrated version: 1alphav1'
Expand Down Expand Up @@ -3726,6 +3731,11 @@ spec:
be used instead or in addition to the sidecarImage.
version: 1alphav1'
type: string
downwardAPI:
description: 'DownwardAPI specifies what kind of data
should be exposed to the binding plugin sidecar. Supported
values: "device-info" version: v1alphav1'
type: string
migration:
description: 'Migration means the VM using the plugin
can be safely migrated version: 1alphav1'
Expand Down
13 changes: 7 additions & 6 deletions pkg/hooks/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,13 @@ type PVC struct {
}

type HookSidecar struct {
Image string `json:"image,omitempty"`
ImagePullPolicy k8sv1.PullPolicy `json:"imagePullPolicy"`
Command []string `json:"command,omitempty"`
Args []string `json:"args,omitempty"`
ConfigMap *ConfigMap `json:"configMap,omitempty"`
PVC *PVC `json:"pvc,omitempty"`
Image string `json:"image,omitempty"`
ImagePullPolicy k8sv1.PullPolicy `json:"imagePullPolicy"`
Command []string `json:"command,omitempty"`
Args []string `json:"args,omitempty"`
ConfigMap *ConfigMap `json:"configMap,omitempty"`
PVC *PVC `json:"pvc,omitempty"`
DownwardAPI v1.NetworkBindingDownwardAPIType `json:"-"`
}

func UnmarshalHookSidecarList(vmiObject *v1.VirtualMachineInstance) (HookSidecarList, error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "go_default_library",
srcs = ["sriov.go"],
importpath = "kubevirt.io/kubevirt/pkg/network/sriov",
srcs = [
"deviceinfo.go",
"sriov.go",
],
importpath = "kubevirt.io/kubevirt/pkg/network/deviceinfo",
visibility = ["//visibility:public"],
deps = [
"//pkg/network/namescheme:go_default_library",
Expand All @@ -17,13 +20,16 @@ go_library(
go_test(
name = "go_default_test",
srcs = [
"sriov_suite_test.go",
"deviceinfo_suite_test.go",
"deviceinfo_test.go",
"sriov_test.go",
],
deps = [
":go_default_library",
"//pkg/libvmi:go_default_library",
"//staging/src/kubevirt.io/api/core/v1:go_default_library",
"//staging/src/kubevirt.io/client-go/testutils:go_default_library",
"//vendor/github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1:go_default_library",
"//vendor/github.com/onsi/ginkgo/v2:go_default_library",
"//vendor/github.com/onsi/gomega:go_default_library",
],
Expand Down
89 changes: 89 additions & 0 deletions pkg/network/deviceinfo/deviceinfo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* This file is part of the KubeVirt project
*
* 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.
*
* Copyright the KubeVirt Authors.
*
*/

package deviceinfo

import (
"encoding/json"
"fmt"

networkv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
v1 "kubevirt.io/api/core/v1"

"kubevirt.io/kubevirt/pkg/network/namescheme"
"kubevirt.io/kubevirt/pkg/network/vmispec"
)

func MapBindingPluginNetworkNameToDeviceInfo(networks []v1.Network,
interfaces []v1.Interface,
networkStatusAnnotationValue string,
bindingPlugins map[string]v1.InterfaceBindingPlugin,
) (map[string]*networkv1.DeviceInfo, error) {
return mapNetworkNameToDeviceInfo(
networks,
networkStatusAnnotationValue,
vmispec.FilterInterfacesSpec(interfaces, func(iface v1.Interface) bool {
if iface.Binding != nil {
if binding, exist := bindingPlugins[iface.Binding.Name]; exist && binding.DownwardAPI == v1.DeviceInfo {
return true
}
}
return false
}),
)
}

func mapNetworkNameToDeviceInfo(networks []v1.Network,
networkStatusAnnotationValue string,
interfaces []v1.Interface,
) (map[string]*networkv1.DeviceInfo, error) {
multusInterfaceNameToNetworkStatus, err := mapMultusInterfaceNameToNetworkStatus(networkStatusAnnotationValue)
if err != nil {
return nil, err
}
networkNameScheme := namescheme.CreateNetworkNameSchemeByPodNetworkStatus(networks, multusInterfaceNameToNetworkStatus)

networkDeviceInfo := map[string]*networkv1.DeviceInfo{}
for _, iface := range interfaces {
multusInterfaceName := networkNameScheme[iface.Name]
if networkStatusEntry, exist := multusInterfaceNameToNetworkStatus[multusInterfaceName]; exist {
networkDeviceInfo[iface.Name] = networkStatusEntry.DeviceInfo
// Note: there is no need to return an error in case the interface doesn't exist,
// it may mean the interface is not plugged yet
}
}
return networkDeviceInfo, nil
}

func mapMultusInterfaceNameToNetworkStatus(networkStatusAnnotationValue string) (map[string]networkv1.NetworkStatus, error) {
if networkStatusAnnotationValue == "" {
return nil, fmt.Errorf("network-status annotation is not present")
}
var networkStatusList []networkv1.NetworkStatus
if err := json.Unmarshal([]byte(networkStatusAnnotationValue), &networkStatusList); err != nil {
return nil, fmt.Errorf("failed to unmarshal network-status annotation: %v", err)
}

multusInterfaceNameToNetworkStatusMap := map[string]networkv1.NetworkStatus{}
for _, networkStatus := range networkStatusList {
multusInterfaceNameToNetworkStatusMap[networkStatus.Interface] = networkStatus
}

return multusInterfaceNameToNetworkStatusMap, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2022 Red Hat, Inc.
* Copyright the KubeVirt Authors.
*
*/

package sriov_test
package deviceinfo_test

import (
"testing"

"kubevirt.io/client-go/testutils"
)

func TestSRIOV(t *testing.T) {
func TestDeviceInfo(t *testing.T) {
testutils.KubeVirtTestSuiteSetup(t)
}
153 changes: 153 additions & 0 deletions pkg/network/deviceinfo/deviceinfo_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* This file is part of the KubeVirt project
*
* 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.
*
* Copyright the KubeVirt Authors.
*
*/
package deviceinfo_test

import (
networkv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

v1 "kubevirt.io/api/core/v1"

"kubevirt.io/kubevirt/pkg/libvmi"
"kubevirt.io/kubevirt/pkg/network/deviceinfo"
)

var _ = Describe("DeviceInfo", func() {

const (
deviceInfoPlugin = "deviceinfo"
nonDeviceInfoPlugin = "non_deviceinfo"
)

networkStatusWithMixedNetworks := `[
{
"name": "kindnet",
"interface": "eth0",
"ips": [
"10.244.1.9"
],
"mac": "3a:7e:42:fa:37:c6",
"default": true,
"dns": {}
},
{
"name": "default/nad1",
"interface": "pod6446d58d6df",
"mac": "8a:37:d9:e7:0f:18",
"dns": {}
},
{
"name": "default/nad2",
"interface": "pod2c26b46b68f",
"dns": {},
"device-info": {
"type": "pci",
"version": "1.0.0",
"pci": {
"pci-address": "0000:65:00.2"
}
}
}
]`

networkStatusWithPrimaryInterfaceOnly := `[
{
"name": "kindnet",
"interface": "eth0",
"ips": [
"10.244.2.131"
],
"mac": "82:cf:7c:98:43:7e",
"default": true,
"dns": {}
}
]`

bindingPlugins := map[string]v1.InterfaceBindingPlugin{
deviceInfoPlugin: {DownwardAPI: v1.DeviceInfo},
nonDeviceInfoPlugin: {},
}

DescribeTable("should return an error",
func(networkStatusAnnotationValue string) {
networks := []v1.Network{*libvmi.MultusNetwork("foo", "default/nad1")}
interfaces := []v1.Interface{newBindingPluginInterface("foo", deviceInfoPlugin)}
_, err := deviceinfo.MapBindingPluginNetworkNameToDeviceInfo(networks, interfaces, networkStatusAnnotationValue, bindingPlugins)
Expect(err).To(HaveOccurred())
},
Entry("when networkStatus annotation is empty", ""),
Entry("when networkStatus annotation has invalid format", "invalid"),
)

DescribeTable("should prepare empty network device info annotation",
func(networkList []v1.Network, interfaceList []v1.Interface, networkStatusAnnotationValue string) {
Expect(deviceinfo.MapBindingPluginNetworkNameToDeviceInfo(
networkList,
interfaceList,
networkStatusAnnotationValue,
bindingPlugins,
)).To(BeEmpty())
},
Entry("when there is no interface with device info binding plugin",
[]v1.Network{
*v1.DefaultPodNetwork(),
*libvmi.MultusNetwork("foo", "default/nad1"),
},
[]v1.Interface{
libvmi.InterfaceDeviceWithMasqueradeBinding(),
newBindingPluginInterface("foo", nonDeviceInfoPlugin),
},
networkStatusWithMixedNetworks,
),
Entry("when the interface is not in the multus status",
[]v1.Network{*libvmi.MultusNetwork("notfoo", "default/nad1")},
[]v1.Interface{newBindingPluginInterface("notfoo", deviceInfoPlugin)},
networkStatusWithPrimaryInterfaceOnly,
),
)

It("should prepare network device info annotation with multiple networks", func() {
networks := []v1.Network{
*v1.DefaultPodNetwork(),
*libvmi.MultusNetwork("boo", "default/nad1"),
*libvmi.MultusNetwork("foo", "default/nad2"),
*libvmi.MultusNetwork("doo", "default/nad3"),
}
interfaces := []v1.Interface{
libvmi.InterfaceDeviceWithMasqueradeBinding(),
libvmi.InterfaceDeviceWithBridgeBinding("boo"),
newBindingPluginInterface("foo", deviceInfoPlugin),
newBindingPluginInterface("doo", deviceInfoPlugin),
}
expectedMap := map[string]*networkv1.DeviceInfo{
"foo": {Type: "pci", Version: "1.0.0", Pci: &networkv1.PciDevice{PciAddress: "0000:65:00.2"}}}
Expect(deviceinfo.MapBindingPluginNetworkNameToDeviceInfo(
networks, interfaces, networkStatusWithMixedNetworks, bindingPlugins,
)).To(Equal(expectedMap))
})
})

func newBindingPluginInterface(name, bindingPlugin string) v1.Interface {
return v1.Interface{
Name: name,
Binding: &v1.PluginBinding{Name: bindingPlugin},
}
}
Loading

0 comments on commit 895075a

Please sign in to comment.