@@ -1045,7 +1045,7 @@ var _ = Describe("[sriov] operator", func() {
10451045 })
10461046
10471047 Describe ("Custom SriovNetworkNodePolicy" , func () {
1048- BeforeEach (func () {
1048+ AfterEach (func () {
10491049 err := namespaces .Clean (operatorNamespace , namespaces .Test , clients , discovery .Enabled ())
10501050 Expect (err ).ToNot (HaveOccurred ())
10511051 WaitForSRIOVStable ()
@@ -1722,6 +1722,111 @@ var _ = Describe("[sriov] operator", func() {
17221722 })
17231723 })
17241724
1725+ Context ("Draining Daemons using SR-IOV" , func () {
1726+ var node string
1727+ resourceName := "drainresource"
1728+ sriovNetworkName := "test-drainnetwork"
1729+ var drainPolicy * sriovv1.SriovNetworkNodePolicy
1730+
1731+ BeforeEach (func () {
1732+ isSingleNode , err := cluster .IsSingleNode (clients )
1733+ Expect (err ).ToNot (HaveOccurred ())
1734+ if isSingleNode {
1735+ // TODO: change this when we add support for draining on single node
1736+ Skip ("This test is not supported on single node as we don't drain on single node" )
1737+ }
1738+
1739+ node = sriovInfos .Nodes [0 ]
1740+ sriovDeviceList , err := sriovInfos .FindSriovDevices (node )
1741+ Expect (err ).ToNot (HaveOccurred ())
1742+ intf := sriovDeviceList [0 ]
1743+ By ("Using device " + intf .Name + " on node " + node )
1744+
1745+ drainPolicy = & sriovv1.SriovNetworkNodePolicy {
1746+ ObjectMeta : metav1.ObjectMeta {
1747+ GenerateName : "test-drainpolicy" ,
1748+ Namespace : operatorNamespace ,
1749+ },
1750+
1751+ Spec : sriovv1.SriovNetworkNodePolicySpec {
1752+ NodeSelector : map [string ]string {
1753+ "kubernetes.io/hostname" : node ,
1754+ },
1755+ Mtu : 1500 ,
1756+ NumVfs : 5 ,
1757+ ResourceName : resourceName ,
1758+ Priority : 99 ,
1759+ NicSelector : sriovv1.SriovNetworkNicSelector {
1760+ PfNames : []string {intf .Name },
1761+ },
1762+ DeviceType : "netdevice" ,
1763+ },
1764+ }
1765+
1766+ err = clients .Create (context .Background (), drainPolicy )
1767+ Expect (err ).ToNot (HaveOccurred ())
1768+
1769+ WaitForSRIOVStable ()
1770+ By ("waiting for the resources to be available" )
1771+ Eventually (func () int64 {
1772+ testedNode , err := clients .CoreV1Interface .Nodes ().Get (context .Background (), node , metav1.GetOptions {})
1773+ Expect (err ).ToNot (HaveOccurred ())
1774+ resNum := testedNode .Status .Allocatable [corev1 .ResourceName ("openshift.io/" + resourceName )]
1775+ allocatable , _ := resNum .AsInt64 ()
1776+ return allocatable
1777+ }, 10 * time .Minute , time .Second ).Should (Equal (int64 (5 )))
1778+
1779+ sriovNetwork := & sriovv1.SriovNetwork {
1780+ ObjectMeta : metav1.ObjectMeta {
1781+ Name : sriovNetworkName ,
1782+ Namespace : operatorNamespace ,
1783+ },
1784+ Spec : sriovv1.SriovNetworkSpec {
1785+ ResourceName : resourceName ,
1786+ IPAM : `{"type":"host-local","subnet":"10.10.10.0/24","rangeStart":"10.10.10.171","rangeEnd":"10.10.10.181"}` ,
1787+ NetworkNamespace : namespaces .Test ,
1788+ }}
1789+
1790+ // We need this to be able to run the connectivity checks on Mellanox cards
1791+ if intf .DeviceID == "1015" {
1792+ sriovNetwork .Spec .SpoofChk = off
1793+ }
1794+
1795+ err = clients .Create (context .Background (), sriovNetwork )
1796+
1797+ Expect (err ).ToNot (HaveOccurred ())
1798+ waitForNetAttachDef ("test-drainnetwork" , namespaces .Test )
1799+
1800+ createTestDaemonSet (node , []string {sriovNetworkName })
1801+ })
1802+
1803+ It ("should reconcile managed VF if status is changed" , func () {
1804+ By ("getting running config-daemon and test pod on the node" )
1805+ daemonTestPod , err := findPodOnNodeWithLabelsAndNamespace (node , namespaces .Test , map [string ]string {"app" : "test" })
1806+ Expect (err ).ToNot (HaveOccurred ())
1807+ daemonConfigPod , err := findPodOnNodeWithLabelsAndNamespace (node , operatorNamespace , map [string ]string {"app" : "sriov-network-config-daemon" })
1808+ Expect (err ).ToNot (HaveOccurred ())
1809+
1810+ By ("deleting the sriov policy to start a drain" )
1811+ err = clients .Delete (context .Background (), drainPolicy )
1812+ Expect (err ).ToNot (HaveOccurred ())
1813+ WaitForSRIOVStable ()
1814+
1815+ tmpDaemon := & appsv1.DaemonSet {}
1816+ By ("Checking the pod owned by a DaemonSet requesting sriov device was deleted " )
1817+ Eventually (func (g Gomega ) bool {
1818+ err = clients .Client .Get (context .Background (), runtimeclient.ObjectKey {Name : daemonTestPod .Name , Namespace : daemonTestPod .Namespace }, tmpDaemon )
1819+ return err != nil && k8serrors .IsNotFound (err )
1820+ }, time .Minute , 5 * time .Second ).Should (BeTrue ())
1821+
1822+ By ("Checking the pod owned by a DaemonSet not requesting an sriov device was not deleted" )
1823+ Consistently (func (g Gomega ) bool {
1824+ err = clients .Client .Get (context .Background (), runtimeclient.ObjectKey {Name : daemonConfigPod .Name , Namespace : daemonConfigPod .Namespace }, tmpDaemon )
1825+ return err != nil && k8serrors .IsNotFound (err )
1826+ }, time .Minute , 10 * time .Second ).Should (BeTrue ())
1827+ })
1828+ })
1829+
17251830 })
17261831})
17271832
@@ -1842,6 +1947,11 @@ func findMainSriovDevice(executorPod *corev1.Pod, sriovDevices []*sriovv1.Interf
18421947
18431948func findUnusedSriovDevices (testNode string , sriovDevices []* sriovv1.InterfaceExt ) ([]* sriovv1.InterfaceExt , error ) {
18441949 createdPod := createCustomTestPod (testNode , []string {}, true , nil )
1950+ defer func () {
1951+ err := clients .Delete (context .Background (), createdPod )
1952+ Expect (err ).ToNot (HaveOccurred ())
1953+ }()
1954+
18451955 filteredDevices := []* sriovv1.InterfaceExt {}
18461956 stdout , _ , err := pod .ExecCommand (clients , createdPod , "ip" , "route" )
18471957 Expect (err ).ToNot (HaveOccurred ())
@@ -1989,6 +2099,24 @@ func isDaemonsetScheduledOnNodes(nodeSelector, daemonsetLabelSelector string) bo
19892099 return true
19902100}
19912101
2102+ func findPodOnNodeWithLabelsAndNamespace (nodeName string , namespace string , labels map [string ]string ) (* corev1.Pod , error ) {
2103+ podList := & corev1.PodList {}
2104+ err := clients .List (context .Background (), podList , runtimeclient .MatchingLabels (labels ), & runtimeclient.ListOptions {Namespace : namespace }, runtimeclient.MatchingFields {"spec.nodeName" : nodeName })
2105+ if err != nil {
2106+ return nil , err
2107+ }
2108+
2109+ if len (podList .Items ) == 0 {
2110+ return nil , fmt .Errorf ("no pod found" )
2111+ }
2112+
2113+ if len (podList .Items ) > 1 {
2114+ return nil , fmt .Errorf ("multiple pods found" )
2115+ }
2116+
2117+ return & podList .Items [0 ], nil
2118+ }
2119+
19922120func createSriovPolicy (sriovDevice string , testNode string , numVfs int , resourceName string ) {
19932121 _ , err := network .CreateSriovPolicy (clients , "test-policy-" , operatorNamespace , sriovDevice , testNode , numVfs , resourceName , "netdevice" )
19942122 Expect (err ).ToNot (HaveOccurred ())
@@ -2017,7 +2145,6 @@ func createCustomTestPod(node string, networks []string, hostNetwork bool, podCa
20172145 node ,
20182146 )
20192147 }
2020-
20212148 if len (podCapabilities ) != 0 {
20222149 if podDefinition .Spec .Containers [0 ].SecurityContext == nil {
20232150 podDefinition .Spec .Containers [0 ].SecurityContext = & corev1.SecurityContext {}
@@ -2034,6 +2161,41 @@ func createCustomTestPod(node string, networks []string, hostNetwork bool, podCa
20342161 return waitForPodRunning (createdPod )
20352162}
20362163
2164+ func createTestDaemonSet (node string , networks []string ) * appsv1.DaemonSet {
2165+ podDefinition := pod .RedefineWithNodeSelector (
2166+ pod .GetDefinition (),
2167+ node ,
2168+ )
2169+
2170+ // remove NET_ADMIN to not have issues in OCP
2171+ podDefinition .Spec .Containers [0 ].SecurityContext = nil
2172+
2173+ daemonDefinition := & appsv1.DaemonSet {
2174+ ObjectMeta : metav1.ObjectMeta {GenerateName : "test-" , Namespace : namespaces .Test },
2175+ Spec : appsv1.DaemonSetSpec {
2176+ Selector : & metav1.LabelSelector {
2177+ MatchLabels : map [string ]string {
2178+ "app" : "test" ,
2179+ },
2180+ },
2181+ Template : corev1.PodTemplateSpec {
2182+ ObjectMeta : metav1.ObjectMeta {
2183+ Labels : map [string ]string {
2184+ "app" : "test" ,
2185+ },
2186+ Annotations : map [string ]string {"k8s.v1.cni.cncf.io/networks" : strings .Join (networks , "," )},
2187+ },
2188+ Spec : podDefinition .Spec ,
2189+ },
2190+ },
2191+ }
2192+
2193+ err := clients .Create (context .Background (), daemonDefinition )
2194+ Expect (err ).ToNot (HaveOccurred ())
2195+
2196+ return waitForDaemonReady (daemonDefinition )
2197+ }
2198+
20372199func pingPod (ip string , nodeSelector string , sriovNetworkAttachment string ) {
20382200 ipProtocolVersion := "6"
20392201 if len (strings .Split (ip , "." )) == 4 {
@@ -2376,6 +2538,18 @@ func waitForPodRunning(p *corev1.Pod) *corev1.Pod {
23762538 return ret
23772539}
23782540
2541+ func waitForDaemonReady (d * appsv1.DaemonSet ) * appsv1.DaemonSet {
2542+ Eventually (func (g Gomega ) bool {
2543+ err := clients .Get (context .Background (), runtimeclient.ObjectKey {Name : d .Name , Namespace : d .Namespace }, d )
2544+ g .Expect (err ).ToNot (HaveOccurred ())
2545+ g .Expect (d .Status .DesiredNumberScheduled ).To (BeNumerically (">" , 0 ))
2546+ g .Expect (d .Status .CurrentNumberScheduled ).To (BeNumerically (">" , 0 ))
2547+ return d .Status .DesiredNumberScheduled == d .Status .NumberReady
2548+ }, 3 * time .Minute , 1 * time .Second ).Should (BeTrue (), "DaemonSet [%s/%s] should have running pods" , d .Namespace , d .Name )
2549+
2550+ return d
2551+ }
2552+
23792553// assertNodeStateHasVFMatching asserts that the given node state has at least one VF matching the given fields
23802554func assertNodeStateHasVFMatching (nodeName string , fields Fields ) {
23812555 EventuallyWithOffset (1 , func (g Gomega ) sriovv1.InterfaceExts {
0 commit comments