Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SecondaryNetwork] Require resourceName annotations for SRIOV #6999

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 26 additions & 7 deletions pkg/agent/secondarynetwork/podwatch/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const (

const (
networkAttachDefAnnotationKey = "k8s.v1.cni.cncf.io/networks"
resourceNameAnnotationKey = "k8s.v1.cni.cncf.io/resourceName"
cniPath = "/opt/cni/bin/"
startIfaceIndex = 1
endIfaceIndex = 101
Expand Down Expand Up @@ -324,8 +325,10 @@ func (pc *PodController) processNextWorkItem() bool {
func (pc *PodController) configureSecondaryInterface(
pod *corev1.Pod,
network *netdefv1.NetworkSelectionElement,
resourceName string,
podCNIInfo *podCNIInfo,
networkConfig *SecondaryNetworkConfig) error {
networkConfig *SecondaryNetworkConfig,
) error {
var ipamResult *ipam.IPAMResult
var ifConfigErr error
if networkConfig.IPAM != nil {
Expand Down Expand Up @@ -357,7 +360,7 @@ func (pc *PodController) configureSecondaryInterface(

switch networkConfig.NetworkType {
case sriovNetworkType:
ifConfigErr = pc.configureSriovAsSecondaryInterface(pod, network, podCNIInfo, int(networkConfig.MTU), &ipamResult.Result)
ifConfigErr = pc.configureSriovAsSecondaryInterface(pod, network, resourceName, podCNIInfo, int(networkConfig.MTU), &ipamResult.Result)
case vlanNetworkType:
if networkConfig.VLAN > 0 {
// Let VLAN ID in the CNI network configuration override the IPPool subnet
Expand All @@ -384,15 +387,15 @@ func (pc *PodController) configurePodSecondaryNetwork(pod *corev1.Pod, networkLi
interfacesConfigured := 0
for _, network := range networkList {
klog.V(2).InfoS("Secondary Network attached to Pod", "network", network, "Pod", klog.KObj(pod))
netDefCRD, err := pc.netAttachDefClient.NetworkAttachmentDefinitions(network.Namespace).Get(context.TODO(), network.Name, metav1.GetOptions{})
netAttachDef, err := pc.netAttachDefClient.NetworkAttachmentDefinitions(network.Namespace).Get(context.TODO(), network.Name, metav1.GetOptions{})
if err != nil {
klog.ErrorS(err, "Failed to get NetworkAttachmentDefinition",
"network", network, "Pod", klog.KRef(pod.Namespace, pod.Name))
savedErr = err
continue
}

cniConfig, err := netdefutils.GetCNIConfig(netDefCRD, "")
cniConfig, err := netdefutils.GetCNIConfig(netAttachDef, "")
if err != nil {
klog.ErrorS(err, "Failed to parse NetworkAttachmentDefinition",
"network", network, "Pod", klog.KRef(pod.Namespace, pod.Name))
Expand All @@ -405,14 +408,30 @@ func (pc *PodController) configurePodSecondaryNetwork(pod *corev1.Pod, networkLi
if networkConfig != nil && networkConfig.Type != cniserver.AntreaCNIType {
// Ignore non-Antrea CNI type.
klog.InfoS("Not Antrea CNI type in NetworkAttachmentDefinition, ignoring",
"NetworkAttachmentDefinition", klog.KObj(netDefCRD), "Pod", klog.KRef(pod.Namespace, pod.Name))
"NetworkAttachmentDefinition", klog.KObj(netAttachDef), "Pod", klog.KRef(pod.Namespace, pod.Name))
} else {
klog.ErrorS(err, "NetworkConfig validation failed",
"NetworkAttachmentDefinition", klog.KObj(netDefCRD), "Pod", klog.KRef(pod.Namespace, pod.Name))
"NetworkAttachmentDefinition", klog.KObj(netAttachDef), "Pod", klog.KRef(pod.Namespace, pod.Name))
}
continue
}

var resourceName string
if networkConfig.NetworkType == sriovNetworkType {
v, ok := netAttachDef.Annotations[resourceNameAnnotationKey]
if !ok {
// This annotation is required for SRIOV devices, otherwise there is
// no way to make sure that we allocate the "right" type of device.
err := fmt.Errorf("missing annotation: %s", resourceNameAnnotationKey)
klog.ErrorS(err, "Invalid NetworkAttachmentDefinition", "NetworkAttachmentDefinition", klog.KObj(netAttachDef))
// It is probably worth retrying as the NetworkAttachmentDefinition
// may eventually be updated with the missing annotation.
savedErr = err
continue
}
resourceName = v
}

// Generate a new interface name, if the secondary interface name was not provided in the
// Pod annotation.
if network.InterfaceRequest == "" {
Expand All @@ -425,7 +444,7 @@ func (pc *PodController) configurePodSecondaryNetwork(pod *corev1.Pod, networkLi
}

// Secondary network information retrieved from API server. Proceed to configure secondary interface now.
if err = pc.configureSecondaryInterface(pod, network, podCNIInfo, networkConfig); err != nil {
if err = pc.configureSecondaryInterface(pod, network, resourceName, podCNIInfo, networkConfig); err != nil {
klog.ErrorS(err, "Secondary interface configuration failed",
"Pod", klog.KRef(pod.Namespace, pod.Name), "interface", network.InterfaceRequest,
"networkType", networkConfig.NetworkType)
Expand Down
130 changes: 107 additions & 23 deletions pkg/agent/secondarynetwork/podwatch/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,27 +78,34 @@ const (
"vlan": {{.VLAN}}
}`

defaultCNIVersion = "0.3.0"
defaultMTU = 1500
sriovDeviceID = "sriov-device-id"
podName = "pod1"
containerID = "container1"
podIP = "1.2.3.4"
networkName = "net"
interfaceName = "eth2"
defaultCNIVersion = "0.3.0"
defaultMTU = 1500
sriovResourceName1 = "intel.com/intel_sriov_netdevice"
sriovResourceName2 = "mellanox.com/mlnx_connectx5"
sriovDeviceID11 = "sriov-device-id-11"
sriovDeviceID12 = "sriov-device-id-12"
sriovDeviceID21 = "sriov-device-id-21"
podName = "pod1"
containerID = "container1"
podIP = "1.2.3.4"
networkName = "net"
interfaceName = "eth2"
)

func testNetwork(name string, networkType networkType) *netdefv1.NetworkAttachmentDefinition {
return testNetworkExt(name, "", "", string(networkType), "", 0, 0, false)
return testNetworkExt(name, "", "", networkType, "", "", 0, 0, false)
}

func testNetworkExt(name, cniVersion, cniType, networkType, ipamType string, mtu, vlan int, noIPAM bool) *netdefv1.NetworkAttachmentDefinition {
func testNetworkExt(name, cniVersion, cniType string, networkType networkType, resourceName, ipamType string, mtu, vlan int, noIPAM bool) *netdefv1.NetworkAttachmentDefinition {
if cniVersion == "" {
cniVersion = defaultCNIVersion
}
if cniType == "" {
cniType = "antrea"
}
if networkType == sriovNetworkType && resourceName == "" {
resourceName = sriovResourceName1
}
if ipamType == "" {
ipamType = ipam.AntreaIPAMType
}
Expand All @@ -109,7 +116,7 @@ func testNetworkExt(name, cniVersion, cniType, networkType, ipamType string, mtu
IPAMType string
MTU int
VLAN int
}{cniVersion, cniType, networkType, ipamType, mtu, vlan}
}{cniVersion, cniType, string(networkType), ipamType, mtu, vlan}

var tmpl *template.Template
if !noIPAM {
Expand All @@ -119,9 +126,14 @@ func testNetworkExt(name, cniVersion, cniType, networkType, ipamType string, mtu
}
var b bytes.Buffer
tmpl.Execute(&b, &data)
annotations := make(map[string]string)
if resourceName != "" {
annotations[resourceNameAnnotationKey] = resourceName
}
return &netdefv1.NetworkAttachmentDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Name: name,
Annotations: annotations,
},
Spec: netdefv1.NetworkAttachmentDefinitionSpec{
Config: b.String(),
Expand Down Expand Up @@ -189,8 +201,11 @@ func testIPAMResult(cidr string, vlan int) *ipam.IPAMResult {
}

func init() {
getPodContainerDeviceIDsFn = func(name string, namespace string) ([]string, error) {
return []string{sriovDeviceID}, nil
getPodContainerDeviceIDsFn = func(name string, namespace string) (map[string][]string, error) {
return map[string][]string{
sriovResourceName1: {sriovDeviceID11, sriovDeviceID12},
sriovResourceName2: {sriovDeviceID21},
}, nil
}
}

Expand Down Expand Up @@ -258,7 +273,7 @@ func TestPodControllerRun(t *testing.T) {
containerNetNs(containerID),
interfaceName,
defaultMTU,
sriovDeviceID,
sriovDeviceID11,
&ipamResult.Result,
).Do(func(string, string, string, string, string, int, string, *current.Result) {
atomic.AddInt32(&interfaceConfigured, 1)
Expand All @@ -277,7 +292,7 @@ func TestPodControllerRun(t *testing.T) {
_, err = client.CoreV1().Pods(testNamespace).Create(context.Background(), pod, metav1.CreateOptions{})
require.NoError(t, err, "error when creating test Pod")

// Wait for ConfigureSriovSecondaryInterface is called.
// Wait for ConfigureSriovSecondaryInterface to be called.
assert.Eventually(t, func() bool {
return atomic.LoadInt32(&interfaceConfigured) == 1
}, 1*time.Second, 10*time.Millisecond)
Expand All @@ -292,7 +307,8 @@ func TestPodControllerRun(t *testing.T) {
containerNetNs(containerID),
interfaceName,
defaultMTU,
"",
// We haven't updated the vfDeviceIDUsageMap, so a different device will be allocated.
sriovDeviceID12,
&ipamResult.Result,
).Do(func(string, string, string, string, string, int, string, *current.Result) {
atomic.AddInt32(&interfaceConfigured, 1)
Expand Down Expand Up @@ -329,7 +345,7 @@ func TestPodControllerRun(t *testing.T) {
mockIPAM.EXPECT().SecondaryNetworkRelease(podOwner)
// CNI Del event.
event.IsAdd = false
// Interfac is not deleted from the interface store, so CNI Del should trigger interface
// Interface is not deleted from the interface store, so CNI Del should trigger interface
// deletion again.
podController.processCNIUpdate(event)
_, exists = cniCache.Load(podKey)
Expand Down Expand Up @@ -455,7 +471,7 @@ func TestConfigurePodSecondaryNetwork(t *testing.T) {
containerNetNs(containerID),
interfaceName,
1500,
sriovDeviceID,
sriovDeviceID11,
&testIPAMResult("148.14.24.100/24", 0).Result,
)
},
Expand Down Expand Up @@ -561,7 +577,7 @@ func TestConfigurePodSecondaryNetwork(t *testing.T) {

if !tc.doNotCreateNetwork {
network1 := testNetworkExt(networkName, tc.cniVersion, tc.cniType,
string(tc.networkType), tc.ipamType, tc.mtu, tc.vlan, tc.noIPAM)
tc.networkType, "", tc.ipamType, tc.mtu, tc.vlan, tc.noIPAM)
pc.netAttachDefClient.NetworkAttachmentDefinitions(testNamespace).Create(context.Background(),
network1, metav1.CreateOptions{})
}
Expand All @@ -579,6 +595,74 @@ func TestConfigurePodSecondaryNetwork(t *testing.T) {

}

func TestConfigurePodSecondaryNetworkMultipleSriovDevices(t *testing.T) {
ctx := context.Background()
element1 := netdefv1.NetworkSelectionElement{
Namespace: testNamespace,
Name: "net1",
InterfaceRequest: "eth10",
}
element2 := netdefv1.NetworkSelectionElement{
Namespace: testNamespace,
Name: "net2",
InterfaceRequest: "eth11",
}
pod, cniInfo := testPod(podName, containerID, podIP, element1, element2)
ctrl := gomock.NewController(t)
pc, _, interfaceConfigurator := testPodControllerStart(ctrl)

network1 := testNetworkExt("net1", "", "", sriovNetworkType, sriovResourceName1, "", 1500, 0, true /* noIPAM */)
pc.netAttachDefClient.NetworkAttachmentDefinitions(testNamespace).Create(ctx, network1, metav1.CreateOptions{})
network2 := testNetworkExt("net2", "", "", sriovNetworkType, sriovResourceName2, "", 1500, 0, true /* noIPAM */)
pc.netAttachDefClient.NetworkAttachmentDefinitions(testNamespace).Create(ctx, network2, metav1.CreateOptions{})

gomock.InOrder(
interfaceConfigurator.EXPECT().ConfigureSriovSecondaryInterface(
podName,
testNamespace,
containerID,
containerNetNs(containerID),
element2.InterfaceRequest,
1500,
sriovDeviceID21,
gomock.Any(),
),
interfaceConfigurator.EXPECT().ConfigureSriovSecondaryInterface(
podName,
testNamespace,
containerID,
containerNetNs(containerID),
element1.InterfaceRequest,
1500,
sriovDeviceID11,
gomock.Any(),
),
)
assert.NoError(t, pc.configurePodSecondaryNetwork(pod, []*netdefv1.NetworkSelectionElement{&element2, &element1}, cniInfo))

podKey := podKeyGet(pod.Name, pod.Namespace)
deviceCache, ok := pc.vfDeviceIDUsageMap.Load(podKey)
require.True(t, ok)
expectedDeviceCache := []podSriovVFDeviceIDInfo{
{
resourceName: sriovResourceName1,
vfDeviceID: sriovDeviceID11,
ifName: element1.InterfaceRequest,
},
{
resourceName: sriovResourceName1,
vfDeviceID: sriovDeviceID12,
ifName: "",
},
{
resourceName: sriovResourceName2,
vfDeviceID: sriovDeviceID21,
ifName: element2.InterfaceRequest,
},
}
assert.ElementsMatch(t, expectedDeviceCache, deviceCache.([]podSriovVFDeviceIDInfo))
}

func TestPodControllerAddPod(t *testing.T) {
pod, _ := testPod(podName, containerID, podIP, netdefv1.NetworkSelectionElement{
Name: networkName,
Expand Down Expand Up @@ -623,7 +707,7 @@ func TestPodControllerAddPod(t *testing.T) {
)
network1 := testNetwork("net1", sriovNetworkType)
testVLAN := 100
network2 := testNetworkExt("net2", "", "", string(vlanNetworkType), "", defaultMTU, testVLAN, false)
network2 := testNetworkExt("net2", "", "", vlanNetworkType, "", "", defaultMTU, testVLAN, false)

podOwner1 := &crdv1beta1.PodOwner{Name: podName, Namespace: testNamespace,
ContainerID: containerID, IFName: "eth10"}
Expand Down Expand Up @@ -771,7 +855,7 @@ func TestPodControllerAddPod(t *testing.T) {
containerNetNs(containerID),
interfaceName,
defaultMTU,
sriovDeviceID,
sriovDeviceID11,
gomock.Any(),
)
mockIPAM.EXPECT().SecondaryNetworkAllocate(gomock.Any(), gomock.Any()).Return(testIPAMResult("148.14.24.100/24", 0), nil)
Expand Down Expand Up @@ -885,7 +969,7 @@ func TestPodControllerAddPod(t *testing.T) {
t.Run("updating deviceID cache per Pod", func(t *testing.T) {
ctrl := gomock.NewController(t)
podController, _, _ := testPodController(ctrl)
_, err := podController.assignUnusedSriovVFDeviceID(podName, testNamespace, interfaceName)
_, err := podController.assignUnusedSriovVFDeviceID(podName, testNamespace, sriovResourceName1, interfaceName)
_, exists := podController.vfDeviceIDUsageMap.Load(podKey)
assert.True(t, exists)
require.NoError(t, err, "error while assigning unused VfDevice ID")
Expand Down
Loading
Loading