From 7ae91ff7b160760a89513e3c0be93da9d2febe6b Mon Sep 17 00:00:00 2001 From: Arti Sood Date: Fri, 7 Aug 2026 14:41:26 -0400 Subject: [PATCH 1/3] Hypershift On Kubevirt Model 1 localnet-as-primary support Add ATTACH_DEFAULT_NETWORK=localnet mode to hypershift-kubevirt-create for deploying KubeVirt hosted clusters with OVN localnet as the primary (and only) network interface, using --attach-default-network=false. Guest VMs connect solely via a localnet NAD on the management cluster's L2 segment (192.168.111.0/24), enabling same-subnet bootstrap and EgressIP verification without a default pod network. Changes: - hypershift-kubevirt-create-commands.sh: add localnet NAD creation, OVN DHCP_Options injection (with dns_server), port security clearing, and ipecho pod deployment for EgressIP source-IP verification - hypershift-kubevirt-create-ref.yaml: add LOCALNET_SUBNET and LOCALNET_ATTACH_DEFAULT env vars, update ATTACH_DEFAULT_NETWORK docs - CNO release-5.0 config: add metal-ds-ipi-ovn-kubevirt-hypershift- localnet-primary debug job on equinix-ocp-hcp --- ...-cluster-network-operator-release-5.0.yaml | 55 +++++++ .../hypershift-kubevirt-create-commands.sh | 148 +++++++++++++++++- .../hypershift-kubevirt-create-ref.yaml | 19 ++- 3 files changed, 214 insertions(+), 8 deletions(-) diff --git a/ci-operator/config/openshift/cluster-network-operator/openshift-cluster-network-operator-release-5.0.yaml b/ci-operator/config/openshift/cluster-network-operator/openshift-cluster-network-operator-release-5.0.yaml index eaebb53d77451..5e8a0b37ec668 100644 --- a/ci-operator/config/openshift/cluster-network-operator/openshift-cluster-network-operator-release-5.0.yaml +++ b/ci-operator/config/openshift/cluster-network-operator/openshift-cluster-network-operator-release-5.0.yaml @@ -362,6 +362,61 @@ tests: steps: cluster_profile: openshift-org-aws workflow: hypershift-kubevirt-conformance +- always_run: false + as: metal-ds-ipi-ovn-kubevirt-hypershift-localnet-primary + capabilities: + - intranet + optional: true + steps: + cluster_profile: equinix-ocp-hcp + env: + ATTACH_DEFAULT_NETWORK: localnet + CNV_SUBSCRIPTION_SOURCE: redhat-operators-v4-20 + HYPERSHIFT_NODE_COUNT: "3" + HYPERSHIFT_NODE_MEMORY: "16" + KUBEVIRT_CSI_INFRA: lvms-vg1 + LOCALNET_ATTACH_DEFAULT: "false" + LOCALNET_SUBNET: 192.168.111.0/24 + pre: + - chain: baremetalds-ofcir-pre + - ref: hypershift-kubevirt-install + - chain: hypershift-kubevirt-baremetalds-lvm + - chain: hypershift-kubevirt-baremetalds-metallb + - ref: hypershift-install + - ref: hypershift-agent-create-config-dns + - ref: hypershift-kubevirt-create + - ref: hypershift-kubevirt-baremetalds-proxy + - ref: hypershift-kubevirt-health-check + - ref: cucushift-installer-reportportal-marker + test: + - as: wait + cli: latest + commands: | + echo "Cluster is ready — localnet-as-primary debug session" + echo "Management cluster KUBECONFIG: \$KUBECONFIG" + echo "Hosted cluster KUBECONFIG: \${SHARED_DIR}/nested_kubeconfig" + echo "" + echo "Localnet-as-primary config:" + echo " --attach-default-network=false" + echo " Localnet subnet: 192.168.111.0/24" + echo " VMs have ONLY localnet interface (eth0)" + echo "" + oc get nodes + echo "" + echo "Hosted cluster nodes:" + KUBECONFIG="${SHARED_DIR}/nested_kubeconfig" oc get nodes 2>/dev/null || echo "(not ready yet)" + echo "" + echo "Sleeping for 18 hours..." + sleep 64800 + echo "Wait complete." + from: stable:cli + resources: + requests: + cpu: 100m + memory: 200Mi + timeout: 18h30m0s + workflow: cucushift-installer-rehearse-baremetalds-ipi-ovn-dualstack-kubevirt-hypershift + timeout: 20h0m0s - always_run: false as: qe-perfscale-aws-ovn-medium-cluster-density optional: true diff --git a/ci-operator/step-registry/hypershift/kubevirt/create/hypershift-kubevirt-create-commands.sh b/ci-operator/step-registry/hypershift/kubevirt/create/hypershift-kubevirt-create-commands.sh index f678c14b9b5fb..87dfbc6ad0b98 100644 --- a/ci-operator/step-registry/hypershift/kubevirt/create/hypershift-kubevirt-create-commands.sh +++ b/ci-operator/step-registry/hypershift/kubevirt/create/hypershift-kubevirt-create-commands.sh @@ -136,7 +136,28 @@ fi oc create namespace "${CLUSTER_NAMESPACE_PREFIX}" --dry-run=client -o yaml | oc apply -f - oc create ns "${CLUSTER_NAMESPACE_PREFIX}-${CLUSTER_NAME}" if [[ -n "${ATTACH_DEFAULT_NETWORK}" ]]; then - oc apply -f - <"${SHARED_DIR}/nested_kubeconfig" +# Post-creation localnet setup: DHCP workaround, port security clearing, ipecho deployment +if [[ "${ATTACH_DEFAULT_NETWORK}" == "localnet" ]]; then + LOCALNET_SUBNET="${LOCALNET_SUBNET:-192.168.111.0/24}" + # Derive gateway IP (.1) from the subnet + LOCALNET_GW=$(echo "${LOCALNET_SUBNET}" | sed 's|\.[0-9]*/|.1|') + + echo "Waiting for all VMIs to be Running..." + for i in $(seq 1 60); do + VMI_RUNNING_COUNT=$(oc get vmi -n "${CLUSTER_NAMESPACE_PREFIX}-${CLUSTER_NAME}" --no-headers 2>/dev/null | grep -c Running || true) + if [[ "${VMI_RUNNING_COUNT}" -ge "${HYPERSHIFT_NODE_COUNT}" ]]; then + echo "All ${VMI_RUNNING_COUNT} VMIs are Running" + break + fi + echo "Waiting for VMIs... (${VMI_RUNNING_COUNT}/${HYPERSHIFT_NODE_COUNT} running) [${i}/60]" + sleep 10 + done + + echo "Configuring OVN DHCP options and clearing port security for localnet LSPs..." + for VMI in $(oc get vmi -n "${CLUSTER_NAMESPACE_PREFIX}-${CLUSTER_NAME}" -o jsonpath='{.items[*].metadata.name}' 2>/dev/null); do + NODE=$(oc get vmi "${VMI}" -n "${CLUSTER_NAMESPACE_PREFIX}-${CLUSTER_NAME}" -o jsonpath='{.status.nodeName}' 2>/dev/null) + if [[ -z "${NODE}" ]]; then + echo "WARNING: Could not find node for VMI ${VMI}, skipping" + continue + fi + + # Find the OVN pod on the node where the VM is scheduled + OVN_POD=$(oc get pods -n openshift-ovn-kubernetes -l app=ovnkube-node \ + --field-selector "spec.nodeName=${NODE}" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) + if [[ -z "${OVN_POD}" ]]; then + echo "WARNING: No ovnkube-node pod found on node ${NODE} for VMI ${VMI}, skipping" + continue + fi + + # Find the localnet Logical Switch Port for this VMI + LSP_NAME=$(oc exec -n openshift-ovn-kubernetes "${OVN_POD}" -c nbdb -- \ + ovn-nbctl --columns=name --bare find Logical_Switch_Port \ + "external_ids:k8s.ovn.org/topology=localnet" 2>/dev/null | head -1) + if [[ -z "${LSP_NAME}" ]]; then + echo "WARNING: No localnet LSP found on node ${NODE} for VMI ${VMI}, skipping" + continue + fi + + # Create DHCP options with router, DNS server, and lease time + DHCP_UUID=$(oc exec -n openshift-ovn-kubernetes "${OVN_POD}" -c nbdb -- \ + ovn-nbctl create DHCP_Options cidr="${LOCALNET_SUBNET}" \ + options='"lease_time"="3500" "router"="'"${LOCALNET_GW}"'" "server_id"="'"${LOCALNET_GW}"'" "server_mac"="c0:ff:ee:00:00:01" "dns_server"="'"${LOCALNET_GW}"'"' \ + 2>/dev/null) + + # Bind the DHCP options to the localnet LSP + oc exec -n openshift-ovn-kubernetes "${OVN_POD}" -c nbdb -- \ + ovn-nbctl lsp-set-dhcpv4-options "${LSP_NAME}" "${DHCP_UUID}" 2>/dev/null + + # Clear port security so EgressIP-SNATed packets can exit + oc exec -n openshift-ovn-kubernetes "${OVN_POD}" -c nbdb -- \ + ovn-nbctl clear Logical_Switch_Port "${LSP_NAME}" port_security 2>/dev/null + + echo "Configured DHCP and cleared port security for VMI ${VMI} on node ${NODE} (LSP: ${LSP_NAME})" + done + + # Deploy ip-echo on the management cluster with localnet NAD + IPECHO_NAMESPACE="egressip-ipecho-${CLUSTER_NAME}" + echo "Deploying ip-echo in dedicated namespace ${IPECHO_NAMESPACE}..." + oc create namespace "${IPECHO_NAMESPACE}" --dry-run=client -o yaml | oc apply -f - + oc label ns "${IPECHO_NAMESPACE}" pod-security.kubernetes.io/enforce=privileged --overwrite 2>/dev/null || true + + # Create a localnet NAD in the ip-echo namespace + oc apply -f - < "${SHARED_DIR}/kubevirt_ipecho_url" + echo "Localnet post-creation setup complete" +fi + echo "${CLUSTER_NAME}" > "${SHARED_DIR}/cluster-name" \ No newline at end of file diff --git a/ci-operator/step-registry/hypershift/kubevirt/create/hypershift-kubevirt-create-ref.yaml b/ci-operator/step-registry/hypershift/kubevirt/create/hypershift-kubevirt-create-ref.yaml index 6e7517701e910..95670314d0a3d 100644 --- a/ci-operator/step-registry/hypershift/kubevirt/create/hypershift-kubevirt-create-ref.yaml +++ b/ci-operator/step-registry/hypershift/kubevirt/create/hypershift-kubevirt-create-ref.yaml @@ -36,9 +36,22 @@ ref: - name: ATTACH_DEFAULT_NETWORK default: "" documentation: |- - if true, config additional network for hostedcluster and attach-default-network true; - if false, config additional network for hostedcluster and attach-default-network false - if the default empty string will skip all additional network config. + Controls network attachment for KubeVirt hosted cluster VMs: + - "true": macvlan additional network, attach-default-network=true + - "false": macvlan additional network, attach-default-network=false + - "localnet": OVN localnet network (configurable via LOCALNET_* vars) + - "": skip additional network config (default, pod network only) + - name: LOCALNET_SUBNET + default: "192.168.111.0/24" + documentation: |- + Subnet for the OVN localnet NAD when ATTACH_DEFAULT_NETWORK=localnet. + Use 192.168.111.0/24 for same-L2 bootstrap (VMs can reach mgmt cluster). + - name: LOCALNET_ATTACH_DEFAULT + default: "false" + documentation: |- + Value for --attach-default-network when ATTACH_DEFAULT_NETWORK=localnet. + false = localnet-as-primary (localnet is primary, no pod network). + true = Model 3 (dual-homed: pod network + localnet). - name: ETCD_STORAGE_CLASS default: "" documentation: |- From dd7b1b52bcb44c44e01ba4516402bb40cb005902 Mon Sep 17 00:00:00 2001 From: Arti Sood Date: Fri, 7 Aug 2026 15:53:25 -0400 Subject: [PATCH 2/3] Reuse e2e-aws-hypershift-ovn-kubevirt job name for localnet rehearsal Replace the existing e2e-aws-hypershift-ovn-kubevirt job definition with the localnet-as-primary config so pj-rehearse can detect it as an affected job and allow rehearsal before merging. --- .../openshift-cluster-network-operator-release-5.0.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ci-operator/config/openshift/cluster-network-operator/openshift-cluster-network-operator-release-5.0.yaml b/ci-operator/config/openshift/cluster-network-operator/openshift-cluster-network-operator-release-5.0.yaml index 5e8a0b37ec668..ce20a2286d5a4 100644 --- a/ci-operator/config/openshift/cluster-network-operator/openshift-cluster-network-operator-release-5.0.yaml +++ b/ci-operator/config/openshift/cluster-network-operator/openshift-cluster-network-operator-release-5.0.yaml @@ -358,12 +358,6 @@ tests: workflow: openshift-e2e-aws-ovn-local-to-shared-gateway-mode-migration - always_run: false as: e2e-aws-hypershift-ovn-kubevirt - optional: true - steps: - cluster_profile: openshift-org-aws - workflow: hypershift-kubevirt-conformance -- always_run: false - as: metal-ds-ipi-ovn-kubevirt-hypershift-localnet-primary capabilities: - intranet optional: true From bad1a2f24c50d87c4a7f63577dcb25fbb482ad47 Mon Sep 17 00:00:00 2001 From: Arti Sood Date: Mon, 10 Aug 2026 08:28:29 -0400 Subject: [PATCH 3/3] Workaround missing pull-secret on build10 by routing to build09 The cluster-secrets-equinix-ocp-hcp secret on build10 is missing the pull-secret key, causing baremetalds-devscripts-setup to fail. Route the e2e-aws-hypershift-ovn-kubevirt job to build09 as a workaround while the DPTP issue is resolved. --- .../openshift-cluster-network-operator-release-5.0.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/ci-operator/config/openshift/cluster-network-operator/openshift-cluster-network-operator-release-5.0.yaml b/ci-operator/config/openshift/cluster-network-operator/openshift-cluster-network-operator-release-5.0.yaml index ce20a2286d5a4..f521bfc9392f5 100644 --- a/ci-operator/config/openshift/cluster-network-operator/openshift-cluster-network-operator-release-5.0.yaml +++ b/ci-operator/config/openshift/cluster-network-operator/openshift-cluster-network-operator-release-5.0.yaml @@ -360,6 +360,7 @@ tests: as: e2e-aws-hypershift-ovn-kubevirt capabilities: - intranet + cluster: build09 optional: true steps: cluster_profile: equinix-ocp-hcp