Skip to content

Commit 26420d3

Browse files
Merge pull request #30197 from RamLavi/add_PreconfiguredUDNAddresses_conflict_ip_mac_e2e
CORENET-6363: Add PreconfiguredUDNAddresses duplicate IP detection tests
2 parents a21249f + fdb0584 commit 26420d3

File tree

2 files changed

+156
-0
lines changed

2 files changed

+156
-0
lines changed

test/extended/networking/kubevirt/client.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import (
1616
"strings"
1717
"time"
1818

19+
"sigs.k8s.io/yaml"
20+
1921
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2022
e2ekubectl "k8s.io/kubernetes/test/e2e/framework/kubectl"
2123

@@ -93,6 +95,81 @@ func (c *Client) GetJSONPath(resource, name, jsonPath string) (string, error) {
9395
}
9496
return strings.TrimSuffix(strings.TrimPrefix(output, `"`), `"`), nil
9597
}
98+
99+
func (c *Client) GetPodsByLabel(labelKey, labelValue string) ([]string, error) {
100+
output, err := c.oc.AsAdmin().Run("get").Args("pods", "-n", c.oc.Namespace(), "-l", fmt.Sprintf("%s=%s", labelKey, labelValue), "-o", "name").Output()
101+
if err != nil {
102+
return nil, err
103+
}
104+
if output == "" {
105+
return []string{}, nil
106+
}
107+
108+
lines := strings.Split(strings.TrimSpace(output), "\n")
109+
podNames := make([]string, 0, len(lines))
110+
for _, line := range lines {
111+
if line != "" {
112+
podName := strings.TrimPrefix(line, "pod/")
113+
podNames = append(podNames, podName)
114+
}
115+
}
116+
return podNames, nil
117+
}
118+
119+
func (c *Client) GetEventsForPod(podName string) ([]string, error) {
120+
output, err := c.oc.AsAdmin().Run("get").Args("events", "-n", c.oc.Namespace(), "--field-selector", fmt.Sprintf("involvedObject.name=%s,involvedObject.kind=Pod", podName), "-o", "custom-columns=MESSAGE:.message", "--no-headers").Output()
121+
if err != nil {
122+
return nil, err
123+
}
124+
if output == "" {
125+
return []string{}, nil
126+
}
127+
lines := strings.Split(strings.TrimSpace(output), "\n")
128+
messages := make([]string, 0, len(lines))
129+
for _, line := range lines {
130+
if line != "" {
131+
messages = append(messages, line)
132+
}
133+
}
134+
return messages, nil
135+
}
136+
137+
type Option func(map[string]interface{})
138+
139+
func (c *Client) CreateVMIFromSpec(vmNamespace, vmName string, vmiSpec map[string]interface{}, opts ...Option) error {
140+
newVMI := map[string]interface{}{
141+
"apiVersion": "kubevirt.io/v1",
142+
"kind": "VirtualMachineInstance",
143+
"metadata": map[string]interface{}{
144+
"name": vmName,
145+
"namespace": vmNamespace,
146+
},
147+
"spec": vmiSpec,
148+
}
149+
150+
for _, opt := range opts {
151+
opt(newVMI)
152+
}
153+
154+
newVMIYAML, err := yaml.Marshal(newVMI)
155+
if err != nil {
156+
return err
157+
}
158+
159+
return c.Apply(string(newVMIYAML))
160+
}
161+
162+
func WithAnnotations(annotations map[string]string) Option {
163+
return func(cr map[string]interface{}) {
164+
metadata, hasMetadata := cr["metadata"].(map[string]interface{})
165+
if !hasMetadata {
166+
metadata = make(map[string]interface{})
167+
cr["metadata"] = metadata
168+
}
169+
metadata["annotations"] = annotations
170+
}
171+
}
172+
96173
func ensureVirtctl(oc *exutil.CLI, dir string) (string, error) {
97174
filepath := filepath.Join(dir, "virtctl")
98175
_, err := os.Stat(filepath)

test/extended/networking/livemigration.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import (
3030
"github.com/openshift/origin/test/extended/util/image"
3131
)
3232

33+
const kvIPRequestsAnnot = "network.kubevirt.io/addresses"
34+
3335
var _ = Describe("[sig-network][OCPFeatureGate:PersistentIPsForVirtualization][Feature:Layer2LiveMigration] Kubevirt Virtual Machines", func() {
3436
// disable automatic namespace creation, we need to add the required UDN label
3537
oc := exutil.NewCLIWithoutNamespace("network-segmentation-e2e")
@@ -303,6 +305,20 @@ var _ = Describe("[sig-network][OCPFeatureGate:PersistentIPsForVirtualization][F
303305
preconfiguredMAC: "02:0A:0B:0C:0D:51",
304306
},
305307
),
308+
Entry(
309+
"[OCPFeatureGate:PreconfiguredUDNAddresses] when the VM with preconfigured IP address is created when the address is already taken",
310+
networkAttachmentConfigParams{
311+
name: nadName,
312+
topology: "layer2",
313+
role: "primary",
314+
allowPersistentIPs: true,
315+
},
316+
kubevirt.FedoraVMWithPreconfiguredPrimaryUDNAttachment,
317+
duplicateVM,
318+
workloadNetworkConfig{
319+
preconfiguredIPs: []string{"203.203.0.100", "2014:100:200::100"},
320+
},
321+
),
306322
)
307323
},
308324
Entry("NetworkAttachmentDefinitions", func(c networkAttachmentConfigParams) {
@@ -526,6 +542,65 @@ func verifyVMMAC(virtClient *kubevirt.Client, vmName, expectedMAC string) {
526542
Should(Equal(expectedMAC))
527543
}
528544

545+
func duplicateVM(cli *kubevirt.Client, vmNamespace, vmName string) {
546+
GinkgoHelper()
547+
duplicateVMName := vmName + "-duplicate"
548+
By(fmt.Sprintf("Duplicating VM %s/%s to %s/%s", vmNamespace, vmName, vmNamespace, duplicateVMName))
549+
550+
vmiSpecJSON, err := cli.GetJSONPath("vmi", vmName, "{.spec}")
551+
Expect(err).NotTo(HaveOccurred())
552+
var vmiSpec map[string]interface{}
553+
Expect(json.Unmarshal([]byte(vmiSpecJSON), &vmiSpec)).To(Succeed())
554+
555+
originalVMIRawAnnotations, err := cli.GetJSONPath("vmi", vmName, "{.metadata.annotations}")
556+
Expect(err).NotTo(HaveOccurred())
557+
558+
originalVMIAnnotations := map[string]string{}
559+
Expect(json.Unmarshal([]byte(originalVMIRawAnnotations), &originalVMIAnnotations)).To(Succeed())
560+
561+
var vmiCreationOptions []kubevirt.Option
562+
var vmiExpectations []func()
563+
if requestedIPs, hasIPRequests := originalVMIAnnotations[kvIPRequestsAnnot]; hasIPRequests {
564+
vmiCreationOptions = append(
565+
vmiCreationOptions,
566+
kubevirt.WithAnnotations(ipRequests(requestedIPs)),
567+
)
568+
vmiExpectations = append(vmiExpectations, func() {
569+
waitForVMPodEventWithMessage(
570+
cli,
571+
vmNamespace,
572+
duplicateVMName,
573+
"IP is already allocated",
574+
2*time.Minute,
575+
)
576+
})
577+
}
578+
Expect(cli.CreateVMIFromSpec(vmNamespace, duplicateVMName, vmiSpec, vmiCreationOptions...)).To(Succeed())
579+
for _, expectation := range vmiExpectations {
580+
expectation()
581+
}
582+
}
583+
584+
func waitForVMPodEventWithMessage(vmClient *kubevirt.Client, vmNamespace, vmName, expectedEventMessage string, timeout time.Duration) {
585+
GinkgoHelper()
586+
By(fmt.Sprintf("Waiting for event containing %q on VM %s/%s virt-launcher pod", expectedEventMessage, vmNamespace, vmName))
587+
588+
Eventually(func(g Gomega) []string {
589+
const vmLabelKey = "vm.kubevirt.io/name"
590+
podNames, err := vmClient.GetPodsByLabel(vmLabelKey, vmName)
591+
g.Expect(err).NotTo(HaveOccurred(), "Failed to get pods by label %s=%s", vmLabelKey, vmName)
592+
g.Expect(podNames).To(HaveLen(1), "Expected exactly one virt-launcher pod for VM %s/%s, but found %d pods: %v", vmNamespace, vmName, len(podNames), podNames)
593+
594+
virtLauncherPodName := podNames[0]
595+
eventMessages, err := vmClient.GetEventsForPod(virtLauncherPodName)
596+
g.Expect(err).NotTo(HaveOccurred(), "Failed to get events for pod %s", virtLauncherPodName)
597+
598+
return eventMessages
599+
}).WithPolling(time.Second).WithTimeout(timeout).Should(
600+
ContainElement(ContainSubstring(expectedEventMessage)),
601+
fmt.Sprintf("Expected to find an event containing %q", expectedEventMessage))
602+
}
603+
529604
func waitForPodsCondition(fr *framework.Framework, pods []*corev1.Pod, conditionFn func(g Gomega, pod *corev1.Pod)) {
530605
for _, pod := range pods {
531606
Eventually(func(g Gomega) {
@@ -696,3 +771,7 @@ func formatAddressesAnnotation(preconfiguredIPs []string) (string, error) {
696771

697772
return string(staticIPs), nil
698773
}
774+
775+
func ipRequests(ips string) map[string]string {
776+
return map[string]string{kvIPRequestsAnnot: ips}
777+
}

0 commit comments

Comments
 (0)