Skip to content
Open
Show file tree
Hide file tree
Changes from 14 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
3 changes: 3 additions & 0 deletions e2e/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,9 @@ func daemonsetDebug(ctx context.Context, deploymentName string, nodeSelector map
Command: []string{"sleep", "infinity"},
SecurityContext: &corev1.SecurityContext{
Privileged: to.Ptr(true),
Capabilities: &corev1.Capabilities{
Add: []corev1.Capability{"SYS_PTRACE", "SYS_RAWIO"},
Comment thread
titilambert marked this conversation as resolved.
Outdated
},
},
},
},
Expand Down
156 changes: 156 additions & 0 deletions e2e/scenario_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3118,3 +3118,159 @@ func Test_ACL_SecondaryNIC_DualStack(t *testing.T) {
},
})
}

func Test_Ubuntu2404_MANA(t *testing.T) {
RunScenario(t, &Scenario{
Description: "Tests that MANA (Accelerated Networking) is properly configured on Ubuntu 24.04 with a V6 SKU",
Tags: Tags{
VMSeriesCoverageTest: true,
MANA: true,
},
Config: Config{
Cluster: ClusterKubenet,
VHD: config.VHDUbuntu2404Gen2Containerd,
BootstrapConfigMutator: func(_ *Cluster, nbc *datamodel.NodeBootstrappingConfiguration) {
nbc.ContainerService.Properties.AgentPoolProfiles[0].VMSize = "Standard_D2ds_v6"
nbc.AgentPoolProfile.VMSize = "Standard_D2ds_v6"
},
VMConfigMutator: func(vmss *armcompute.VirtualMachineScaleSet) {
vmss.SKU.Name = to.Ptr("Standard_D2ds_v6")
enableAcceleratedNetworking(vmss)
vmss.Properties.VirtualMachineProfile.StorageProfile.OSDisk.DiffDiskSettings = nil
},
Validator: func(ctx context.Context, s *Scenario) {
ValidateMANA(ctx, s)
},
Comment thread
titilambert marked this conversation as resolved.
Outdated
Comment thread
titilambert marked this conversation as resolved.
Outdated
},
})
}

func Test_Ubuntu2204_MANA(t *testing.T) {
RunScenario(t, &Scenario{
Description: "Tests that MANA (Accelerated Networking) is properly configured on Ubuntu 22.04 with a V6 SKU",
Tags: Tags{
VMSeriesCoverageTest: true,
MANA: true,
},
Config: Config{
Cluster: ClusterKubenet,
VHD: config.VHDUbuntu2204Gen2Containerd,
BootstrapConfigMutator: func(_ *Cluster, nbc *datamodel.NodeBootstrappingConfiguration) {
nbc.ContainerService.Properties.AgentPoolProfiles[0].VMSize = "Standard_D2ds_v6"
nbc.AgentPoolProfile.VMSize = "Standard_D2ds_v6"
},
VMConfigMutator: func(vmss *armcompute.VirtualMachineScaleSet) {
vmss.SKU.Name = to.Ptr("Standard_D2ds_v6")
enableAcceleratedNetworking(vmss)
vmss.Properties.VirtualMachineProfile.StorageProfile.OSDisk.DiffDiskSettings = nil
},
Validator: func(ctx context.Context, s *Scenario) {
ValidateMANA(ctx, s)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

related to my previous comment, maybe dropping the validator override would be cleaner?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

},
Comment thread
titilambert marked this conversation as resolved.
Outdated
Comment thread
titilambert marked this conversation as resolved.
Outdated
},
})
}

func Test_AzureLinuxV3_MANA(t *testing.T) {
RunScenario(t, &Scenario{
Description: "Tests that MANA (Accelerated Networking) is properly configured on Azure Linux V3 with a V6 SKU",
Tags: Tags{
VMSeriesCoverageTest: true,
MANA: true,
},
Config: Config{
Cluster: ClusterKubenet,
VHD: config.VHDAzureLinuxV3Gen2,
BootstrapConfigMutator: func(_ *Cluster, nbc *datamodel.NodeBootstrappingConfiguration) {
nbc.ContainerService.Properties.AgentPoolProfiles[0].VMSize = "Standard_D2ds_v6"
nbc.AgentPoolProfile.VMSize = "Standard_D2ds_v6"
},
VMConfigMutator: func(vmss *armcompute.VirtualMachineScaleSet) {
vmss.SKU.Name = to.Ptr("Standard_D2ds_v6")
enableAcceleratedNetworking(vmss)
vmss.Properties.VirtualMachineProfile.StorageProfile.OSDisk.DiffDiskSettings = nil
},
Validator: func(ctx context.Context, s *Scenario) {
ValidateMANA(ctx, s)
},
Comment thread
titilambert marked this conversation as resolved.
Outdated
Comment thread
titilambert marked this conversation as resolved.
Outdated
},
})
}

func Test_Ubuntu2404_MANA_V7(t *testing.T) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we have dedicated tests for hardware, we have a suite that runs on the V7 or V8 hardware. we shouldnt be duplicating the tests by hardware I think.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I synced up with @lilypan26 and I added an helper which return the min SKU needed. with that we are currently testing with V6 (with the default SKU at V5), and the day we move to V7, it will use it, so we will have nothing todo...

RunScenario(t, &Scenario{
Description: "Tests that MANA (Accelerated Networking) is properly configured on Ubuntu 24.04 with a V7 SKU",
Tags: Tags{
VMSeriesCoverageTest: true,
MANA: true,
},
Config: Config{
Cluster: ClusterKubenet,
VHD: config.VHDUbuntu2404Gen2Containerd,
UseNVMe: true,
BootstrapConfigMutator: func(_ *Cluster, nbc *datamodel.NodeBootstrappingConfiguration) {
nbc.ContainerService.Properties.AgentPoolProfiles[0].VMSize = "Standard_D2ads_v7"
nbc.AgentPoolProfile.VMSize = "Standard_D2ads_v7"
},
VMConfigMutator: func(vmss *armcompute.VirtualMachineScaleSet) {
vmss.SKU.Name = to.Ptr("Standard_D2ads_v7")
enableAcceleratedNetworking(vmss)
},
Validator: func(ctx context.Context, s *Scenario) {
ValidateMANA(ctx, s)
},
},
})
}

func Test_Ubuntu2204_MANA_V7(t *testing.T) {
RunScenario(t, &Scenario{
Description: "Tests that MANA (Accelerated Networking) is properly configured on Ubuntu 22.04 with a V7 SKU",
Tags: Tags{
VMSeriesCoverageTest: true,
MANA: true,
},
Config: Config{
Cluster: ClusterKubenet,
VHD: config.VHDUbuntu2204Gen2Containerd,
UseNVMe: true,
BootstrapConfigMutator: func(_ *Cluster, nbc *datamodel.NodeBootstrappingConfiguration) {
nbc.ContainerService.Properties.AgentPoolProfiles[0].VMSize = "Standard_D2ads_v7"
nbc.AgentPoolProfile.VMSize = "Standard_D2ads_v7"
},
VMConfigMutator: func(vmss *armcompute.VirtualMachineScaleSet) {
vmss.SKU.Name = to.Ptr("Standard_D2ads_v7")
enableAcceleratedNetworking(vmss)
},
Validator: func(ctx context.Context, s *Scenario) {
ValidateMANA(ctx, s)
},
},
})
}

func Test_AzureLinuxV3_MANA_V7(t *testing.T) {
RunScenario(t, &Scenario{
Description: "Tests that MANA (Accelerated Networking) is properly configured on Azure Linux V3 with a V7 SKU",
Tags: Tags{
VMSeriesCoverageTest: true,
MANA: true,
},
Config: Config{
Cluster: ClusterKubenet,
VHD: config.VHDAzureLinuxV3Gen2,
UseNVMe: true,
BootstrapConfigMutator: func(_ *Cluster, nbc *datamodel.NodeBootstrappingConfiguration) {
nbc.ContainerService.Properties.AgentPoolProfiles[0].VMSize = "Standard_D2ads_v7"
nbc.AgentPoolProfile.VMSize = "Standard_D2ads_v7"
},
VMConfigMutator: func(vmss *armcompute.VirtualMachineScaleSet) {
vmss.SKU.Name = to.Ptr("Standard_D2ads_v7")
enableAcceleratedNetworking(vmss)
},
Validator: func(ctx context.Context, s *Scenario) {
ValidateMANA(ctx, s)
},
},
})
}
1 change: 1 addition & 0 deletions e2e/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type Tags struct {
VHDCaching bool
MockAzureChinaCloud bool
VMSeriesCoverageTest bool
MANA bool
}

// MatchesFilters checks if the Tags struct matches all given filters.
Expand Down
8 changes: 8 additions & 0 deletions e2e/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ func ValidateCommonLinux(ctx context.Context, s *Scenario) {
ValidateLeakedSecrets(ctx, s)
ValidateIPTablesCompatibleWithCiliumEBPF(ctx, s)
ValidateRxBufferDefault(ctx, s)

// Validate MANA (Accelerated Networking) when hardware is present.
// Skipped for scenarios tagged MANA to avoid running the same checks twice (those scenarios call ValidateMANA explicitly).
// MANA is the standard network adapter on V5+ VM series.
if hasMANAHardware(ctx, s) && !s.Tags.MANA {
Comment thread
titilambert marked this conversation as resolved.
Outdated
ValidateMANA(ctx, s)
}
Comment thread
Copilot marked this conversation as resolved.
Comment thread
Copilot marked this conversation as resolved.

ValidateKernelLogs(ctx, s)
ValidateWaagentLog(ctx, s)
ValidateScriptlessCSECmd(ctx, s)
Expand Down
106 changes: 106 additions & 0 deletions e2e/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (

"github.com/Azure/agentbaker/e2e/components"
"github.com/Azure/agentbaker/e2e/config"
"github.com/Azure/agentbaker/e2e/toolkit"
"github.com/Azure/agentbaker/pkg/agent"
"github.com/Azure/agentbaker/pkg/agent/datamodel"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -2811,6 +2812,111 @@ func ValidateRxBufferDefault(ctx context.Context, s *Scenario) {
ValidateNetworkInterfaceConfig(ctx, s, customNicConfig)
}

// ValidateMANAPCIDevice checks that the MANA PCI device is exposed to the VM.
// MANA hardware is identified by PCI device ID 0x00ba (Microsoft Corporation).
func ValidateMANAPCIDevice(ctx context.Context, s *Scenario) {
s.T.Helper()
defer toolkit.LogStep(s.T, "validating MANA PCI device is present")()
cmd := "grep -Rqi '^0x00ba$' /sys/bus/pci/devices/*/device 2>/dev/null"
execScriptOnVMForScenarioValidateExitCode(ctx, s, cmd, 0,
"MANA PCI device (0x00ba) not found in /sys/bus/pci/devices")
}
Comment thread
titilambert marked this conversation as resolved.

// ValidateMANADriverLoaded checks that the MANA Ethernet driver (mana) is loaded
// in the running kernel. For built-in drivers they appear in modules.builtin;
// for loadable modules they must be present in lsmod.
func ValidateMANADriverLoaded(ctx context.Context, s *Scenario) {
s.T.Helper()
defer toolkit.LogStep(s.T, "validating MANA kernel driver is loaded")()
cmd := `lsmod | grep -q '^mana ' || grep -q '/mana\.ko' /lib/modules/$(uname -r)/modules.builtin`
execScriptOnVMForScenarioValidateExitCode(ctx, s, cmd, 0,
"MANA kernel driver (mana) not found in lsmod or modules.builtin")
}

// ValidateMANAVFBonded checks that the MANA Virtual Function (VF) interface exists
// and is properly bonded to the primary eth0 interface.
// When Accelerated Networking is enabled with MANA, a VF interface should appear
// as a subordinate (SLAVE) of eth0. The VF name varies by VM generation:
// - V5: enP* (e.g., enP30832p0s0)
// - V6+: ens1 or enp0s0
func ValidateMANAVFBonded(ctx context.Context, s *Scenario) {
s.T.Helper()
defer toolkit.LogStep(s.T, "validating MANA VF is bonded to eth0")()
// Look for any interface that has "master eth0" in ip link output,
// indicating it is bonded as a VF to the primary synthetic NIC.
cmd := `ip link show | grep 'master eth0'`
result := execScriptOnVMForScenarioValidateExitCode(ctx, s, cmd, 0,
"no VF interface found bonded to eth0 — accelerated networking may not be working")
Comment thread
titilambert marked this conversation as resolved.
s.T.Logf("MANA VF bonding: %s", strings.TrimSpace(result.stdout))
}

// ValidateMANATrafficFlowing checks that network traffic is actually flowing through
// the MANA Virtual Function rather than the slower synthetic (NetVSC) path.
// It sends HTTP requests from a pod to the node's default gateway and verifies
// that the VF TX packet counters increase by at least that amount.
func ValidateMANATrafficFlowing(ctx context.Context, s *Scenario) {
s.T.Helper()
defer toolkit.LogStep(s.T, "validating traffic is flowing through MANA VF")()

const requestCount = 10
getVFTxPackets := `val=$(sudo ethtool -S eth0 | awk '/^[[:space:]]*vf_tx_packets:/{print $2; exit}'); [ -n "$val" ] && echo "$val" || { echo "vf_tx_packets not found in ethtool -S eth0 output" >&2; exit 1; }`
// Read VF tx counter before generating traffic
resultBefore := execScriptOnVMForScenarioValidateExitCode(ctx, s, getVFTxPackets, 0,
"could not read VF tx packet counter from ethtool -S eth0")
Comment thread
titilambert marked this conversation as resolved.
countBefore, err := strconv.Atoi(strings.TrimSpace(resultBefore.stdout))
require.NoError(s.T, err, "failed to parse vf_tx_packets before value %q", resultBefore.stdout)
s.T.Logf("MANA VF tx packets before: %d", countBefore)

// Generate traffic from a pod on this node using curl to the node's default
// gateway. We use vf_tx_packets (not rx) so the test passes regardless of
// whether the target responds — what matters is that outbound packets from
// the pod traverse the MANA VF path. curl is pre-installed in the Mariner
// debug image, so no package install is needed.
gatewayResult := execScriptOnVMForScenarioValidateExitCode(ctx, s,
"ip route | awk '/default/{print $3}'", 0,
"could not determine default gateway from ip route")
gatewayIP := strings.TrimSpace(gatewayResult.stdout)
require.NotEmpty(s.T, gatewayIP, "default gateway IP is empty")
s.T.Logf("MANA traffic test: using gateway %s as target", gatewayIP)

// The "; true" ensures exit 0 regardless of curl's result — the gateway has
// no HTTP server so connections will fail, but TCP SYN packets still traverse
// the VF (incrementing vf_tx_packets). The real assertion is the counter delta below.
curlCmd := fmt.Sprintf("for i in $(seq 1 %d); do curl -s -o /dev/null -m 1 http://%s/ 2>/dev/null; done; true", requestCount, gatewayIP)
execOnVMForScenarioOnUnprivilegedPod(ctx, s, curlCmd)
Comment thread
titilambert marked this conversation as resolved.
Comment thread
titilambert marked this conversation as resolved.
Comment thread
titilambert marked this conversation as resolved.
Comment thread
titilambert marked this conversation as resolved.
Comment on lines +2885 to +2886

// Read VF tx counter after generating traffic
resultAfter := execScriptOnVMForScenarioValidateExitCode(ctx, s, getVFTxPackets, 0,
"could not read VF tx packet counter from ethtool -S eth0")
Comment thread
titilambert marked this conversation as resolved.
countAfter, err := strconv.Atoi(strings.TrimSpace(resultAfter.stdout))
require.NoError(s.T, err, "failed to parse vf_tx_packets after value %q", resultAfter.stdout)

delta := countAfter - countBefore
s.T.Logf("MANA VF tx packets after: %d (delta: %d, expected >= %d)", countAfter, delta, requestCount)

require.GreaterOrEqual(s.T, delta, requestCount,
"vf_tx_packets increased by %d but expected at least %d \u2014 traffic may not be flowing through the MANA VF", delta, requestCount)
Comment thread
titilambert marked this conversation as resolved.
}

// ValidateMANA runs all MANA (Microsoft Azure Network Adapter) checks.
// It verifies that the MANA PCI device is present, the kernel driver is loaded,
// the VF interface is bonded to eth0, and traffic is flowing through the VF.
func ValidateMANA(ctx context.Context, s *Scenario) {
s.T.Helper()
ValidateMANAPCIDevice(ctx, s)
ValidateMANADriverLoaded(ctx, s)
ValidateMANAVFBonded(ctx, s)
ValidateMANATrafficFlowing(ctx, s)
}

// hasMANAHardware checks if the VM has MANA PCI hardware available.
// Returns true if the MANA device (0x00ba) is found in sysfs.
// This is used to conditionally run MANA validations on VMs that support it.
func hasMANAHardware(ctx context.Context, s *Scenario) bool {
result := execScriptOnVMForScenario(ctx, s, "grep -Rqi '^0x00ba$' /sys/bus/pci/devices/*/device 2>/dev/null")
return result.exitCode == "0"
}
Comment thread
titilambert marked this conversation as resolved.
Comment thread
titilambert marked this conversation as resolved.
Comment thread
titilambert marked this conversation as resolved.
Comment thread
titilambert marked this conversation as resolved.
Comment on lines +2915 to +2918

// ValidateKernelLogs checks kernel logs for critical errors across multiple categories:
// - Kernel panics/crashes (panic, oops, call trace, BUG, etc.)
// - CPU lockups/stalls (soft/hard lockup, RCU stall, hung task, watchdog)
Expand Down
14 changes: 14 additions & 0 deletions e2e/vmss.go
Original file line number Diff line number Diff line change
Expand Up @@ -1110,6 +1110,20 @@ func addSecondaryNIC(vmss *armcompute.VirtualMachineScaleSet) {
)
}

// enableAcceleratedNetworking explicitly enables Accelerated Networking on the
// primary NIC of the VMSS. This ensures MANA (Microsoft Azure Network Adapter)
// is active on the VM, which is required for V5+ VM series.
func enableAcceleratedNetworking(vmss *armcompute.VirtualMachineScaleSet) {

@djsly djsly Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not all sku support AcceleratedNetworking, will this fail on SKU where AN isn't supported ? or will it silently accept ?

what is the consequences if AN isn't working on a VM where Mana driver is present ? Are we able to detect that ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not all sku support AcceleratedNetworking, will this fail on SKU where AN isn't supported ? or will it silently accept ?
I just tested it locally (not commited) and it's failing:

func Test_MANA_UnsupportedSKU(t *testing.T) {
	RunScenario(t, &Scenario{
		Description: "Verifies that enabling AcceleratedNetworking on an unsupported SKU fails at VMSS creation",
		Tags: Tags{
			MANA: true,
		},
		Config: Config{
			Cluster: ClusterKubenet,
			VHD:     config.VHDUbuntu2404Gen2Containerd,
			BootstrapConfigMutator: func(_ *Cluster, nbc *datamodel.NodeBootstrappingConfiguration) {
				nbc.ContainerService.Properties.AgentPoolProfiles[0].VMSize = "Standard_B1s"
				nbc.AgentPoolProfile.VMSize = "Standard_B1s"
			},
			VMConfigMutator: func(vmss *armcompute.VirtualMachineScaleSet) {
				vmss.SKU.Name = to.Ptr("Standard_B1s")
				vmss.Properties.VirtualMachineProfile.StorageProfile.OSDisk.DiffDiskSettings = nil
				enableAcceleratedNetworking(vmss)
			},
		},
	})
}

gives

$ TAGS_TO_RUN="name=Test_MANA_UnsupportedSKU/default" ./e2e-local.sh
...
    test_helpers.go:362: [106.277s] 🔴 FAIL:
                Error Trace:    /home/thicohen/gits/github/azure/AgentBaker/e2e/test_helpers.go:362
                                                        /home/thicohen/gits/github/azure/AgentBaker/e2e/test_helpers.go:263
                                                        /home/thicohen/gits/github/azure/AgentBaker/e2e/test_helpers.go:89
                Error:          Received unexpected error:
                                PUT https://management.azure.com/subscriptions/8ecadfc9-d1a3-4ea4-b844-0d9f87e4d7c8/resourceGroups/MC_abe2e-westus3_abe2e-kubenet-v5-d04f2_westus3/providers/Microsoft.Compute/virtualMachineScaleSets/rnwk-2026-07-09-manaunsupportedskudefault
                                --------------------------------------------------------------------------------
                                RESPONSE 400: 400 Bad Request
                                ERROR CODE: VMSizeIsNotPermittedToEnableAcceleratedNetworkingForVmss
                                --------------------------------------------------------------------------------
                                {
                                  "error": {
                                    "details": [],
                                    "code": "VMSizeIsNotPermittedToEnableAcceleratedNetworkingForVmss",
                Test:           Test_MANA_UnsupportedSKU/default
                Messages:       create vmss "rnwk-2026-07-09-manaunsupportedskudefault", check scenario-logs/Test_MANA_UnsupportedSKU/default for vm logs

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@djsly for your second question, the tests ValidateMANAVFBonded and ValidateMANATrafficFlowing are there to validate the device is working correctly.
ValidateMANAVFBonded ensures the VF interface is bonded to eth0 and ValidateMANATrafficFlowing validates that packets are actually flowing through the VF path.

Regarding network performance tests, I considered adding throughput validation but couldn't find documented bandwidth guarantees specific to AN/MANA per SKU, and throughput tests could be flaky in shared CI environments.

primaryNIC, err := getVMSSNICConfig(vmss)
if err != nil {
panic(fmt.Sprintf("enableAcceleratedNetworking: unable to get primary NIC config: %v", err))
}
if primaryNIC.Properties == nil {
primaryNIC.Properties = &armcompute.VirtualMachineScaleSetNetworkConfigurationProperties{}
}
primaryNIC.Properties.EnableAcceleratedNetworking = to.Ptr(true)
Comment thread
titilambert marked this conversation as resolved.
}
Comment thread
Copilot marked this conversation as resolved.

// addDualStackSecondaryNIC appends a secondary (non-primary) NIC with both IPv4 and IPv6
// IP configurations to the VMSS model, using the same subnet as the primary NIC.
func addDualStackSecondaryNIC(vmss *armcompute.VirtualMachineScaleSet) {
Expand Down
Loading