Documentation
¶
Index ¶
- Constants
- Variables
- func BaselineImagePath(baseImagePath string) string
- func BootedImagePath(baseImagePath string) string
- func CreateICMPv4EchoPacket(srcIP, dstIP net.IP, id, seq uint16, payload []byte) []byte
- func CreateTCPIPv4Packet(srcIP, dstIP net.IP, payload []byte, opts *TCPPacketOpts) []byte
- func CreateTCPIPv6Packet(srcIP, dstIP net.IP, payload []byte, opts *TCPPacketOpts) []byte
- func DataplaneConfig(opts DataplaneOptions) string
- func DefaultControlplaneConfig() string
- func DefaultForwardConfig() string
- func DefaultRouteConfig() string
- func ExpandCIDR(cidr string) []string
- func Fragment(pkt gopacket.Packet, fragSize int) ([]gopacket.Packet, error)
- func Fragment6(pkt gopacket.Packet, fragSize int) ([]gopacket.Packet, error)
- func HasBaselineSnapshot() bool
- func HasBootedSnapshot(overlayPath string) bool
- func IsDebugEnabled() bool
- func KVMAvailable() bool
- func MarkBaselineSaved()
- func MustParseMAC(mac string) net.HardwareAddr
- func MustPrefixRange(prefix string) (start, end string)
- func NewPacket(opts *gopacket.SerializeOptions, layerBuilders ...LayerBuilder) (gopacket.Packet, error)
- func OverlayHasSnapshot(imagePath, name string) bool
- func Payload(content string, repeat int) []byte
- func PoolSize() int
- func PortRange(start, end uint16) []uint16
- func PrefixRange(prefix string) (start, end string, err error)
- func ShouldKeepVMAlive() bool
- func SnapshotImagePath(baseImagePath string, snapshotName string) string
- func VMReadyTimeout() time.Duration
- type ARPBuilder
- type ARPOption
- func ARPHwDst(mac string) ARPOption
- func ARPHwLen(hwLen uint8) ARPOption
- func ARPHwSrc(mac string) ARPOption
- func ARPHwType(hwType uint16) ARPOption
- func ARPOperation(op uint16) ARPOption
- func ARPPDst(ip string) ARPOption
- func ARPPLen(pLen uint8) ARPOption
- func ARPPSrc(ip string) ARPOption
- func ARPPType(pType uint16) ARPOption
- type CLIManager
- func (c *CLIManager) ExecuteCommand(command string) (string, error)
- func (c *CLIManager) ExecuteCommandWithTimeout(command string, timeout time.Duration) (string, error)
- func (c *CLIManager) ExecuteCommands(commands ...string) ([]string, error)
- func (c *CLIManager) ExecuteCommandsSeparately(commands ...string) ([]string, []error)
- func (c *CLIManager) WithLog(log *zap.SugaredLogger) *CLIManager
- type CLIOption
- type Config
- type DataplaneOptions
- type Dot1QBuilder
- type Dot1QOption
- type EtherOption
- type EthernetBuilder
- type Framework
- type FrameworkOption
- type GREBuilder
- type GREOption
- func GREChecksum(checksum uint16) GREOption
- func GREChecksumPresent(present bool) GREOption
- func GREKey(key uint32) GREOption
- func GREKeyPresent(present bool) GREOption
- func GREProtocol(proto layers.EthernetType) GREOption
- func GRERawFlags(flags uint16) GREOption
- func GRERoutingPresent(present bool) GREOption
- func GRESeq(seq uint32) GREOption
- func GRESeqPresent(present bool) GREOption
- func GREVersion(version uint8) GREOption
- type GuestPaths
- type Harness
- type HarnessConfig
- type ICMPBuilder
- type ICMPOption
- type ICMPv6Builder
- func ICMPv6(opts ...ICMPv6Option) *ICMPv6Builder
- func ICMPv6DestUnreach(opts ...ICMPv6Option) *ICMPv6Builder
- func ICMPv6EchoReply(opts ...ICMPv6Option) *ICMPv6Builder
- func ICMPv6EchoRequest(opts ...ICMPv6Option) *ICMPv6Builder
- func ICMPv6PacketTooBig(opts ...ICMPv6Option) *ICMPv6Builder
- func ICMPv6ParamProblem(opts ...ICMPv6Option) *ICMPv6Builder
- func ICMPv6TimeExceeded(opts ...ICMPv6Option) *ICMPv6Builder
- type ICMPv6EchoBuilder
- type ICMPv6EchoOption
- type ICMPv6NeighborAdvertisementBuilder
- type ICMPv6NeighborSolicitationBuilder
- type ICMPv6Option
- type ICMPv6RouterAdvertisementBuilder
- type ICMPv6RouterSolicitationBuilder
- type IPSecESPBuilder
- type IPSecESPOption
- type IPv4Builder
- type IPv4Option
- func IPDst(ip string) IPv4Option
- func IPFlags(flags layers.IPv4Flag) IPv4Option
- func IPFragOffset(offset uint16) IPv4Option
- func IPId(id uint16) IPv4Option
- func IPProto(proto layers.IPProtocol) IPv4Option
- func IPSrc(ip string) IPv4Option
- func IPTOS(tos uint8) IPv4Option
- func IPTTL(ttl uint8) IPv4Option
- func IPv4ChecksumRaw(cs uint16) IPv4Option
- func IPv4DummyOptions(size int) IPv4Option
- func IPv4IHL(ihl uint8) IPv4Option
- func IPv4Length(length uint16) IPv4Option
- func IPv4Options(opts []IPv4OptionDef) IPv4Option
- func IPv4RawOptions(data []byte) IPv4Option
- type IPv4OptionDef
- type IPv6Builder
- type IPv6ExtHdrDestOptBuilder
- type IPv6ExtHdrDestOptOption
- type IPv6FragmentBuilder
- type IPv6FragmentOption
- type IPv6Option
- type LayerBuilder
- type MPLSBuilder
- type MPLSOption
- type PacketInfo
- type PacketParser
- type QEMUManager
- func (q *QEMUManager) AbortSerial()
- func (q *QEMUManager) AttachSerial() (net.Conn, func() error, error)
- func (q *QEMUManager) EnableSSHForward()
- func (q *QEMUManager) ForceStop()
- func (q *QEMUManager) GetStdin() io.WriteCloser
- func (q *QEMUManager) IsVMReady() bool
- func (q *QEMUManager) ReconnectSerial() error
- func (q *QEMUManager) RestartSerial() error
- func (q *QEMUManager) RestoreBooted() error
- func (q *QEMUManager) RestoreSnapshot(name string) error
- func (q *QEMUManager) SSHPort() int
- func (q *QEMUManager) SaveBootedOverlay() (string, error)
- func (q *QEMUManager) SaveSnapshot(name string) error
- func (q *QEMUManager) SaveSnapshotOverlay(name string) (string, error)
- func (q *QEMUManager) SendMonitorCommand(cmd string) (string, error)
- func (q *QEMUManager) Start() (bool, error)
- func (q *QEMUManager) Stop() error
- func (q *QEMUManager) WaitForReady(timeout time.Duration) error
- type RawBuilder
- type ReplyBudget
- type SocketClient
- func (sc *SocketClient) Close() error
- func (sc *SocketClient) Connect() error
- func (sc *SocketClient) GetSocketPort() int
- func (sc *SocketClient) ReceiveAllPackets(timeout time.Duration, dumpPath string) ([][]byte, error)
- func (sc *SocketClient) ReceiveAllPacketsUnfiltered(timeout time.Duration, dumpPath string) ([][]byte, error)
- func (sc *SocketClient) ReceivePacket(timeout time.Duration, dumpPath string) ([]byte, error)
- func (sc *SocketClient) ResetConnection() error
- func (sc *SocketClient) SendAndReceivePacket(budget *ReplyBudget, packet []byte, dumpPath string) ([]byte, error)
- func (sc *SocketClient) SendPacket(packet []byte, dumpPath string) error
- func (sc *SocketClient) SendPackets(packets [][]byte, dumpPath string) error
- func (sc *SocketClient) WithLog(log *zap.SugaredLogger) *SocketClient
- type SocketClientOption
- type TCPBuilder
- type TCPOption
- func TCPAck(ack uint32) TCPOption
- func TCPChecksumRaw(cs uint16) TCPOption
- func TCPDataOffset(offset uint8) TCPOption
- func TCPDport(port uint16) TCPOption
- func TCPDummyOptions(size int) TCPOption
- func TCPFlags(flags string) TCPOption
- func TCPOptions(opts []TCPOptionDef) TCPOption
- func TCPSeq(seq uint32) TCPOption
- func TCPSport(port uint16) TCPOption
- func TCPUrgent(urg uint16) TCPOption
- func TCPWindow(win uint16) TCPOption
- type TCPOptionDef
- type TCPPacketOpts
- type TestFramework
- func (f *TestFramework) AbortGuestSerial()
- func (f *TestFramework) AdoptRunningConfig(dataplaneConfig string, controlplaneConfig string)
- func (f *TestFramework) AttachSerial() (net.Conn, func() error, error)
- func (f *TestFramework) CommonConfigCommands() []string
- func (f *TestFramework) CreateConfigFile(name string, config string) error
- func (f *TestFramework) CreateForwardConfig(config string) error
- func (f *TestFramework) ExecuteCommand(command string) (string, error)
- func (f *TestFramework) ExecuteCommandWithTimeout(command string, timeout time.Duration) (string, error)
- func (f *TestFramework) ExecuteCommands(commands ...string) ([]string, error)
- func (f *TestFramework) ExecuteCommandsSeparately(commands ...string) ([]string, []error)
- func (f *TestFramework) ExportCurrentOverlay(dst string) error
- func (f *TestFramework) ForTest(t *testing.T) *TestFramework
- func (f *TestFramework) GetSocketClient(ifaceIndex int) (*SocketClient, error)
- func (f *TestFramework) GetSocketPaths() []string
- func (f *TestFramework) Mount9P() error
- func (f *TestFramework) PrepareLocalStorage() error
- func (f *TestFramework) ResetConnections()
- func (f *TestFramework) RestartGuestSerial() error
- func (f *TestFramework) RestartYANET() error
- func (f *TestFramework) RestoreAndReconnect(snapshot string) error
- func (f *TestFramework) RestoreBooted() error
- func (f *TestFramework) RestoreClean(snapshot string) error
- func (f *TestFramework) Run(name string, fn func(fw *TestFramework, t *testing.T)) bool
- func (f *TestFramework) RunWith(snapshot string, name string, fn func(fw *TestFramework, t *testing.T)) bool
- func (f *TestFramework) SSHPort() int
- func (f *TestFramework) SaveSnapshot(name string) error
- func (f *TestFramework) SaveSnapshotKeepUnmounted(name string) error
- func (f *TestFramework) SendPacketAndCapture(inputIfaceIndex int, outputIfaceIndex int, packet []byte, ...) ([]byte, error)
- func (f *TestFramework) SendPacketAndCaptureAll(inputIfaceIndex int, outputIfaceIndex int, packet []byte, ...) ([][]byte, error)
- func (f *TestFramework) SendPacketAndCaptureAllUnfiltered(inputIfaceIndex int, outputIfaceIndex int, packet []byte, ...) ([][]byte, error)
- func (f *TestFramework) SendPacketAndParse(inputIfaceIndex int, outputIfaceIndex int, packet []byte, ...) (*PacketInfo, *PacketInfo, error)
- func (f *TestFramework) SendPacketAndParseAll(inputIfaceIndex int, outputIfaceIndex int, packet []byte, ...) ([]*PacketInfo, error)
- func (f *TestFramework) SendPacketsAndParseAll(inputIfaceIndex int, outputIfaceIndex int, packets [][]byte, ...) ([]*PacketInfo, error)
- func (f *TestFramework) Start() (bool, error)
- func (f *TestFramework) StartLogTailers() error
- func (f *TestFramework) StartYANET(dataplaneConfig string, controlplaneConfig string) error
- func (f *TestFramework) Stop() error
- func (f *TestFramework) Unmount9P() error
- func (f *TestFramework) WaitForDatapathReady(timeout time.Duration) error
- func (f *TestFramework) WaitForReady(timeout time.Duration) error
- func (f *TestFramework) WaitOutputPresent(cmd string, checker func(string) bool, timeout time.Duration) error
- func (f *TestFramework) WriteGuestFile(path string, contents string) error
- type UDPBuilder
- type UDPOption
- type VMPool
- func (p *VMPool) Acquire() *TestFramework
- func (p *VMPool) ForEachParallel(fn func(idx int, fw *TestFramework) error) error
- func (p *VMPool) ForceStop()
- func (p *VMPool) Release(fw *TestFramework)
- func (p *VMPool) Shutdown() error
- func (p *VMPool) Size() int
- func (p *VMPool) StartAll() error
- func (p *VMPool) StopAllCPU()
- func (p *VMPool) WaitAllReady(timeout time.Duration) error
Constants ¶
const ( // MAC addresses used in test framework SrcMAC = "52:54:00:6b:ff:a1" DstMAC = "52:54:00:6b:ff:a5" // IP addresses used in test framework VMIPv4Host = "203.0.113.14" VMIPv4Gateway = "203.0.113.1" VMIPv6Gateway = "fe80::1" VMIPv6Host = "fe80::5054:ff:fe6b:ffa5" // CLI tool paths (9P defaults, used in single-VM mode) CLIBasePath = "/mnt/target/release" CLIRoute = CLIBasePath + "/yanet-cli-route" CLIRouteMPLS = CLIBasePath + "/yanet-cli-route-mpls" CLINAT64 = CLIBasePath + "/yanet-cli-nat64" CLIACL = CLIBasePath + "/yanet-cli-acl" CLIFWState = CLIBasePath + "/yanet-cli-fwstate" CLIFWStateMap = CLIBasePath + "/yanet-cli-fwstatemap" CLIPipeline = CLIBasePath + "/yanet-cli-pipeline" CLIFunction = CLIBasePath + "/yanet-cli-function" CLIDevicePlain = CLIBasePath + "/yanet-cli-device-plain" CLIDecap = CLIBasePath + "/yanet-cli-decap" CLIForward = CLIBasePath + "/yanet-cli-forward" CLIGeneric = CLIBasePath + "/yanet-cli" )
const ( // BootedSnapshotName is the name of the QEMU snapshot saved after a // clean boot. When a pool VM's overlay contains this snapshot, Start() // restores it instantly via -loadvm instead of waiting ~44s for boot. BootedSnapshotName = "booted" )
const ReadTimeout = 2 * time.Second
ReadTimeout is the per-read timeout SendAndReceivePacket waits for a single reply.
Measured against a CPU-starved CI runner across roughly 38000 sends, reply latency was p50 10ms and p99 53ms, and the single miss observed still arrived at 114ms rather than being lost. The timeout gives roughly 18x headroom over that observed maximum.
Variables ¶
var CLIBinaryNames = []string{
"yanet-cli",
"yanet-cli-route", "yanet-cli-route-mpls",
"yanet-cli-nat64", "yanet-cli-acl", "yanet-cli-blackhole",
"yanet-cli-fwstate", "yanet-cli-fwstatemap", "yanet-cli-pipeline", "yanet-cli-function",
"yanet-cli-device-plain", "yanet-cli-device-vlan",
"yanet-cli-decap", "yanet-cli-forward",
"yanet-cli-common", "yanet-cli-dscp", "yanet-cli-counters",
"yanet-cli-pdump", "yanet-cli-inspect",
}
CLIBinaryNames is the canonical list of all CLI binaries installed in the YANET target/release directory. Used by StartYANET verification, PrepareLocalStorage, and test assertions to keep binary lists in sync.
var CmpStdOpts = []cmp.Option{ cmpopts.IgnoreUnexported( layers.Ethernet{}, layers.Dot1Q{}, layers.IPv4{}, layers.IPv6{}, layers.TCP{}, layers.UDP{}, layers.ICMPv4{}, layers.ICMPv6{}, layers.ICMPv6Echo{}, layers.ICMPv6RouterSolicitation{}, layers.ICMPv6RouterAdvertisement{}, layers.ICMPv6NeighborSolicitation{}, layers.ICMPv6NeighborAdvertisement{}, layers.IPv6Destination{}, layers.IPv6Routing{}, layers.IPv6HopByHop{}, layers.IPv6Fragment{}, layers.GRE{}, layers.MPLS{}, layers.ARP{}, gopacket.DecodeFailure{}, ), cmpopts.IgnoreFields(layers.Ethernet{}, "BaseLayer"), cmpopts.IgnoreFields(layers.Dot1Q{}, "BaseLayer"), cmpopts.IgnoreFields(layers.IPv4{}, "BaseLayer"), cmpopts.IgnoreFields(layers.IPv6{}, "BaseLayer"), cmpopts.IgnoreFields(layers.TCP{}, "BaseLayer"), cmpopts.IgnoreFields(layers.UDP{}, "BaseLayer"), cmpopts.IgnoreFields(layers.ICMPv4{}, "BaseLayer"), cmpopts.IgnoreFields(layers.ICMPv6{}, "BaseLayer"), cmpopts.IgnoreFields(layers.ICMPv6Echo{}, "BaseLayer"), cmpopts.IgnoreFields(layers.ICMPv6RouterSolicitation{}, "BaseLayer"), cmpopts.IgnoreFields(layers.ICMPv6RouterAdvertisement{}, "BaseLayer"), cmpopts.IgnoreFields(layers.ICMPv6NeighborSolicitation{}, "BaseLayer"), cmpopts.IgnoreFields(layers.ICMPv6NeighborAdvertisement{}, "BaseLayer"), cmpopts.IgnoreFields(layers.IPv6Destination{}, "BaseLayer"), cmpopts.IgnoreFields(layers.IPv6Routing{}, "BaseLayer"), cmpopts.IgnoreFields(layers.IPv6HopByHop{}, "BaseLayer"), cmpopts.IgnoreFields(layers.IPv6Fragment{}, "BaseLayer"), cmpopts.IgnoreFields(layers.GRE{}, "BaseLayer"), cmpopts.IgnoreFields(layers.MPLS{}, "BaseLayer"), cmpopts.IgnoreFields(layers.ARP{}, "BaseLayer"), }
CmpStdOpts provides standard cmp.Diff options for comparing gopacket layers, ensuring consistent packet comparison across functional tests.
These options:
- Ignore unexported fields in gopacket layer structs (internal state)
- Ignore BaseLayer fields (gopacket internal bookkeeping)
- Ignore DecodeFailure unexported fields (for malformed packet handling)
Usage:
diff := cmp.Diff(expectedPkt.Layers(), actualPkt.Layers(), framework.CmpStdOpts...)
if diff != "" {
t.Errorf("Packet mismatch (-want +got):\n%s", diff)
}
var CommonConfigCommands = (&TestFramework{Paths: DefaultGuestPaths()}).CommonConfigCommands()
CommonConfigCommands is the package-level variable for backward compatibility. Uses default 9P paths. In pool mode tests use fw.CommonConfigCommands() instead.
var ErrCaptureTimeout = errors.New("packet capture timed out")
ErrCaptureTimeout indicates that packet transmission succeeded but no packet was captured before the receive deadline.
Functions ¶
func BaselineImagePath ¶
BaselineImagePath returns the versioned path to the cached baseline snapshot.
func BootedImagePath ¶
BootedImagePath returns the versioned path to the booted snapshot image.
func CreateICMPv4EchoPacket ¶
CreateICMPv4EchoPacket builds a serialized Ethernet/IPv4/ICMP Echo Request.
func CreateTCPIPv4Packet ¶
func CreateTCPIPv4Packet(srcIP, dstIP net.IP, payload []byte, opts *TCPPacketOpts) []byte
CreateTCPIPv4Packet builds a serialized Ethernet/IPv4/TCP packet. Pass nil for opts to use defaults (SrcMAC/DstMAC from framework constants, SrcPort 12345, DstPort 80, PSH+ACK flags).
func CreateTCPIPv6Packet ¶
func CreateTCPIPv6Packet(srcIP, dstIP net.IP, payload []byte, opts *TCPPacketOpts) []byte
CreateTCPIPv6Packet builds a serialized Ethernet/IPv6/TCP packet. Pass nil for opts to use defaults (same as CreateTCPIPv4Packet).
func DataplaneConfig ¶
func DataplaneConfig(opts DataplaneOptions) string
DataplaneConfig returns the baseline dataplane YAML used by functional tests, optionally requesting runtime module plugins via opts.
func DefaultControlplaneConfig ¶
func DefaultControlplaneConfig() string
DefaultControlplaneConfig returns the baseline controlplane YAML used by functional tests.
func DefaultForwardConfig ¶
func DefaultForwardConfig() string
DefaultForwardConfig returns the baseline forward.yaml used by functional tests.
func DefaultRouteConfig ¶
func DefaultRouteConfig() string
DefaultRouteConfig returns the baseline route0.yaml used by functional tests.
func ExpandCIDR ¶
ExpandCIDR expands a CIDR notation to all IP addresses in the subnet This mimics Scapy's behavior where IP(dst="172.20.29.5/30") generates packets for 172.20.29.5, 172.20.29.6, 172.20.29.7, 172.20.29.8
func Fragment ¶
Fragment fragments an IPv4 packet according to RFC 791 fragSize is the maximum size of each fragment (including Ethernet/VLAN/IPv4 headers)
func Fragment6 ¶
Fragment6 fragments an IPv6 packet according to RFC 8200 fragSize is the maximum size of each fragment (including IPv6 header and fragment header)
func HasBaselineSnapshot ¶
func HasBaselineSnapshot() bool
HasBaselineSnapshot returns true if the "baseline" VM snapshot is available for per-test state isolation via RunWith("baseline", ...).
func HasBootedSnapshot ¶
HasBootedSnapshot checks if the given overlay file contains a snapshot named "booted". Returns true if the snapshot exists.
func IsDebugEnabled ¶
func IsDebugEnabled() bool
IsDebugEnabled checks if debug mode is enabled for tests. Returns true if the YANET_TEST_DEBUG environment variable is set.
Example:
if IsDebugEnabled() {
log.Info("Debug mode enabled")
// Enable verbose logging
}
func KVMAvailable ¶
func KVMAvailable() bool
KVMAvailable reports whether the current process can use the Linux KVM device.
func MarkBaselineSaved ¶
func MarkBaselineSaved()
MarkBaselineSaved records that the "baseline" VM snapshot was successfully created. Called once from TestMain after setup completes.
func MustParseMAC ¶
func MustParseMAC(mac string) net.HardwareAddr
MustParseMAC parses a MAC address string and panics if parsing fails. This utility function is designed for use with known-good MAC address constants where parsing failure indicates a programming error rather than runtime input error.
Parameters:
- mac: MAC address string in standard format (e.g., "52:54:00:6b:ff:a1")
Returns:
- net.HardwareAddr: Parsed hardware address
Panics:
- If the MAC address string is malformed or invalid
Example:
hwAddr := MustParseMAC("52:54:00:6b:ff:a1")
func MustPrefixRange ¶
MustPrefixRange is PrefixRange, panicking on a malformed prefix.
For call sites converting a compile-time-known CIDR literal, matching this package's existing MustParseMAC panic-on-literal convention.
func NewPacket ¶
func NewPacket(opts *gopacket.SerializeOptions, layerBuilders ...LayerBuilder) (gopacket.Packet, error)
NewPacket creates a new gopacket.Packet from the given LayerBuilder chain, inferring protocol numbers and EtherType/VLAN types from layer order and leaving checksums/lengths untouched unless a custom layer requests otherwise. If opts is nil, default options (FixLengths: true, ComputeChecksums: true) are used.
func OverlayHasSnapshot ¶
OverlayHasSnapshot returns true if the given qcow2 image contains a snapshot with the given name.
func PrefixRange ¶
PrefixRange converts a CIDR prefix into its masked start and end address strings, matching the FIB YAML's range-native "start"/"end" keys.
It masks host bits before taking the start address, so an unmasked prefix such as "10.0.0.5/24" yields "10.0.0.0" rather than "10.0.0.5" as the range start. The end address is derived from the same masked network.
func ShouldKeepVMAlive ¶
func ShouldKeepVMAlive() bool
ShouldKeepVMAlive checks if the VM should be kept running after tests. Returns true if the YANET_KEEP_VM_ALIVE environment variable is set. When enabled, the QEMU process will not be killed, allowing manual connection to the serial console for debugging.
Example:
if ShouldKeepVMAlive() {
log.Info("VM will remain running for manual debugging")
// Skip QEMU process termination
}
func SnapshotImagePath ¶
SnapshotImagePath returns the path to the cached image containing the named snapshot next to the base image.
func VMReadyTimeout ¶
VMReadyTimeout returns how long to wait for a VM to reach the shell prompt.
It defaults to 120 seconds and is overridable via YANET_VM_READY_TIMEOUT, a Go duration string such as "5m" or "300s". An unset, empty, unparseable, or non-positive value falls back to the default, which slow TCG (no-KVM) runners routinely need to raise.
Types ¶
type ARPBuilder ¶
type ARPBuilder struct {
// contains filtered or unexported fields
}
func ARP ¶
func ARP(opts ...ARPOption) *ARPBuilder
func (*ARPBuilder) Build ¶
func (b *ARPBuilder) Build() gopacket.SerializableLayer
type CLIManager ¶
type CLIManager struct {
// contains filtered or unexported fields
}
CLIManager handles YANET CLI operations within a QEMU virtual machine environment. It provides thread-safe command execution capabilities through the VM's serial console, with support for output buffering, command synchronization, and proper cleanup of control characters from terminal output.
The manager uses markers to reliably detect command completion and extract return codes, ensuring accurate command execution status reporting even in concurrent scenarios.
func NewCLIManager ¶
func NewCLIManager(qemu *QEMUManager, opts ...CLIOption) (*CLIManager, error)
NewCLIManager creates and initializes a new CLI manager instance for executing commands within a QEMU virtual machine. The manager provides thread-safe command execution with proper output handling and error reporting.
Parameters:
- qemu: A QEMUManager instance representing the target virtual machine
- opts: Optional functional options for customizing the CLI manager behavior
Returns:
- *CLIManager: A configured CLI manager instance
- error: An error if initialization fails or options cannot be applied
func (*CLIManager) ExecuteCommand ¶
func (c *CLIManager) ExecuteCommand(command string) (string, error)
ExecuteCommand executes a single CLI command within the QEMU virtual machine via the serial console interface. The method ensures thread-safe execution, proper output collection, and reliable command completion detection using unique markers.
The execution process includes:
- VM readiness verification
- Command wrapping with start/end markers for reliable parsing
- Output buffering and cleanup of control characters
- Return code extraction and error handling
Parameters:
- command: The shell command to execute in the virtual machine
Returns:
- string: The cleaned command output (stdout/stderr combined)
- error: An error if the VM is not ready, command fails, or timeout occurs
Example:
output, err := cli.ExecuteCommand("ls -la /etc")
if err != nil {
log.Fatalf("Command failed: %v", err)
}
fmt.Println("Directory listing:", output)
func (*CLIManager) ExecuteCommandWithTimeout ¶
func (c *CLIManager) ExecuteCommandWithTimeout(command string, timeout time.Duration) (string, error)
ExecuteCommandWithTimeout is like ExecuteCommand but with a custom timeout.
func (*CLIManager) ExecuteCommands ¶
func (c *CLIManager) ExecuteCommands(commands ...string) ([]string, error)
ExecuteCommands executes multiple CLI commands sequentially within the QEMU virtual machine. Each command is executed in order, and execution stops at the first command that returns an error.
This method is useful for executing a series of related commands where each command may depend on the success of previous ones, such as configuration setup or multi-step operations.
Parameters:
- commands: Variable number of shell commands to execute sequentially
Returns:
- []string: Slice of command outputs in execution order (may be partial if error occurs)
- error: An error from the first failed command, or nil if all commands succeed
Example:
outputs, err := cli.ExecuteCommands(
"mkdir -p /tmp/test",
"echo 'hello' > /tmp/test/file.txt",
"cat /tmp/test/file.txt",
)
if err != nil {
log.Fatalf("Command sequence failed: %v", err)
}
func (*CLIManager) ExecuteCommandsSeparately ¶
func (c *CLIManager) ExecuteCommandsSeparately(commands ...string) ([]string, []error)
ExecuteCommandsSeparately runs every command to completion regardless of individual failures, pairing each output with its own error. A diagnostics dump uses this so one failed probe (say, catting a log the crashed process never wrote) cannot hide the outputs of the rest.
func (*CLIManager) WithLog ¶
func (c *CLIManager) WithLog(log *zap.SugaredLogger) *CLIManager
WithLog creates a new CLIManager instance with a different logger while sharing the same underlying connection (inner state). This allows each test to have its own logging context while sharing the CLI manager connection.
Parameters:
- log: Logger to use for this CLI manager instance
Returns:
- *CLIManager: A new CLI manager instance with the specified logger
Example:
namedCLI := cli.WithLog(logger.Named("test1"))
type CLIOption ¶
type CLIOption func(*CLIManager) error
CLIOption defines functional options for configuring CLIManager instances. This pattern allows for flexible initialization with optional parameters while maintaining backward compatibility.
func CLIWithLog ¶
func CLIWithLog(log *zap.SugaredLogger) CLIOption
CLIWithLog configures the CLIManager to use the specified logger for debugging and monitoring command execution. If not provided, a no-op logger is used by default.
Parameters:
- log: A zap.SugaredLogger instance for logging CLI operations
Returns:
- CLIOption: A functional option that sets the logger
type Config ¶
type Config struct {
Name string
QEMUImage string // Path to the QEMU virtual machine image file
ProjectRoot string // Canonical project root for build and target paths
}
Config contains essential configuration parameters for initializing the test framework. It specifies the QEMU virtual machine image and working directory for test execution.
type DataplaneOptions ¶
DataplaneOptions customizes the baseline dataplane configuration a Harness boots with.
PluginDir and Modules drive the dataplane's runtime plugin loader: when PluginDir is set the dataplane scans it for module .so plugins and loads each name listed in Modules. Leaving both empty produces a configuration that relies solely on the modules statically linked into the dataplane binary.
type Dot1QBuilder ¶
type Dot1QBuilder struct {
// contains filtered or unexported fields
}
func Dot1Q ¶
func Dot1Q(opts ...Dot1QOption) *Dot1QBuilder
func (*Dot1QBuilder) Build ¶
func (b *Dot1QBuilder) Build() gopacket.SerializableLayer
type Dot1QOption ¶
func VLANId ¶
func VLANId(id uint16) Dot1QOption
func VLANType ¶
func VLANType(etherType layers.EthernetType) Dot1QOption
type EtherOption ¶
func EtherDst ¶
func EtherDst(mac string) EtherOption
func EtherSrc ¶
func EtherSrc(mac string) EtherOption
func EtherType ¶
func EtherType(etherType layers.EthernetType) EtherOption
type EthernetBuilder ¶
type EthernetBuilder struct {
// contains filtered or unexported fields
}
func Ether ¶
func Ether(opts ...EtherOption) *EthernetBuilder
func (*EthernetBuilder) Build ¶
func (b *EthernetBuilder) Build() gopacket.SerializableLayer
type Framework ¶
type Framework struct {
// contains filtered or unexported fields
}
Framework is a safe wrapper that prevents direct access to framework methods. It provides two ways to access the framework:
- Global() - returns TestFramework with testName="global" for global operations in TestMain
- ForTest(t) - returns TestFramework bound to *testing.T for test-specific operations
This design ensures that framework methods cannot be called without proper context.
Example usage:
var globalFramework *Framework
func TestMain(m *testing.M) {
fw, _ := New(config)
globalFramework = fw
globalFramework.Global().Start()
defer globalFramework.Global().Stop()
m.Run()
}
func TestMyFeature(t *testing.T) {
fw := globalFramework.ForTest(t)
fw.Run("SubTest", func(fw *TestFramework, t *testing.T) {
// Use fw for framework operations, t for assertions
})
}
func New ¶
func New(config *Config, opts ...FrameworkOption) (*Framework, error)
New creates and initializes a new Framework instance with the specified configuration and optional functional parameters. The framework sets up all necessary components including QEMU VM management, CLI operations, and packet processing capabilities.
The initialization process includes:
- Working directory creation and validation
- QEMU manager setup with the specified VM image
- CLI manager initialization for VM command execution
- Packet parser setup for network analysis
- Socket client cache initialization
Parameters:
- config: Required configuration containing VM image path and working directory
- opts: Optional functional options for customizing framework behavior
Returns:
- *Framework: Fully initialized framework wrapper
- error: An error if initialization fails or configuration is invalid
Example:
config := &Config{
Name: "main",
QEMUImage: "/path/to/vm-image.qcow2",
}
fw, err := New(config, WithLog(logger))
if err != nil {
log.Fatalf("Failed to create framework: %v", err)
}
func (*Framework) ForTest ¶
func (f *Framework) ForTest(t *testing.T) *TestFramework
ForTest creates a TestFramework instance bound to the provided *testing.T. This method should be called at the beginning of each test function to create a test-specific framework instance with automatic test name tracking.
Parameters:
- t: The *testing.T instance for the current test
Returns:
- *TestFramework: A test-bound framework instance
Example:
func TestMyFeature(t *testing.T) {
fw := globalFramework.ForTest(t)
fw.Run("SubTest", func(fw *TestFramework, t *testing.T) {
// Test code here
})
}
func (*Framework) Global ¶
func (f *Framework) Global() *TestFramework
Global returns the underlying TestFramework with testName="global" for global operations. This method should only be used in TestMain for framework lifecycle management (Start, Stop, StartYANET, etc.).
Returns:
- *TestFramework: The internal framework instance with testName="global"
Example:
func TestMain(m *testing.M) {
fw, _ := New(config)
globalFramework = fw
gfw := fw.Global()
gfw.Start()
defer gfw.Stop()
m.Run()
}
type FrameworkOption ¶
type FrameworkOption func(*TestFramework) error
FrameworkOption defines functional options for configuring TestFramework instances. This pattern enables flexible initialization with optional parameters while maintaining backward compatibility and clean API design.
func WithLog ¶
func WithLog(log *zap.SugaredLogger) FrameworkOption
WithLog configures the TestFramework to use the specified logger for debugging and monitoring test execution. This functional option allows detailed logging of framework operations, packet flows, and VM interactions.
Parameters:
- log: A zap.SugaredLogger instance for structured logging
Returns:
- FrameworkOption: A functional option that sets the logger
type GREBuilder ¶
type GREBuilder struct {
// contains filtered or unexported fields
}
func GRE ¶
func GRE(opts ...GREOption) *GREBuilder
func (*GREBuilder) Build ¶
func (b *GREBuilder) Build() gopacket.SerializableLayer
type GREOption ¶
type GREOption func(*GREBuilder)
func GREChecksum ¶
func GREChecksumPresent ¶
func GREKeyPresent ¶
func GREProtocol ¶
func GREProtocol(proto layers.EthernetType) GREOption
func GRERawFlags ¶
func GRERoutingPresent ¶
func GRESeqPresent ¶
func GREVersion ¶
type GuestPaths ¶
type GuestPaths struct {
CLIBase string // directory with yanet-cli-* binaries
BuildDir string // directory with yanet-dataplane, yanet-controlplane
PluginDir string // directory with dataplane module .so plugins
ConfigDir string // directory for config files
LogDir string // directory for log files
DPDKDevbindDir string // directory containing dpdk-devbind.py
ForwardYAML string // path to forward.yaml
LocalMode bool // true when paths are on guest tmpfs (pool/snapshot mode)
}
GuestPaths holds all guest-side filesystem paths used by the framework. In single-VM mode these point to 9P mounts. In pool/snapshot mode they point to /tmp/yanet/ (local tmpfs) so that no YANET process holds open fids on 9P mounts, allowing savevm/loadvm to succeed.
func DefaultGuestPaths ¶
func DefaultGuestPaths() GuestPaths
DefaultGuestPaths returns the standard 9P-backed paths used in single-VM mode.
func LocalGuestPaths ¶
func LocalGuestPaths() GuestPaths
LocalGuestPaths returns paths on local tmpfs inside the guest. Used in pool/snapshot mode after PrepareLocalStorage() copies files from 9P mounts to /tmp/yanet/.
func (GuestPaths) CLI ¶
func (p GuestPaths) CLI(name string) string
CLI returns the full path to a CLI binary by name.
type Harness ¶
type Harness struct {
// contains filtered or unexported fields
}
Harness owns a booted, baseline-configured VM pool shared by the tests of one functional-test package, together with the YANET configuration used to restore a VM to that baseline.
func SetupHarness ¶
func SetupHarness(config HarnessConfig) (_ *Harness, cleanup func(), err error)
SetupHarness prepares a baseline VM pool for a functional-test package.
It ensures the baseline template overlay exists (bootstrapping it from the booted template when missing), starts the pool, waits for readiness, and pauses idle VM CPUs. The returned cleanup releases the harness logger; the caller owns Shutdown of the returned Harness.
func (*Harness) Restore ¶
func (m *Harness) Restore(fw *TestFramework) error
Restore restores fw to a working YANET baseline without depending on the testing package. Lab tools use this method to get the same fast-path and fallback behavior as functional tests.
The fast path restores the "baseline" snapshot via RestoreClean, resets connections, and checks datapath readiness. The fallback restores the "preyanet" snapshot and starts YANET from scratch. Functional tests that pass no hooks get a fast path that differs from the older RestoreBooted contract (which used RestoreAndReconnect); the fallback path is unchanged.
func (*Harness) RestoreBooted ¶
func (m *Harness) RestoreBooted(t *testing.T, fw *TestFramework)
RestoreBooted restores fw to a working YANET state.
It tries the fast path (baseline snapshot with YANET already running) first, and falls back to the slow path (preyanet snapshot plus a fresh StartYANET) only when the baseline restore fails.
func (*Harness) WithBootedVM ¶
func (m *Harness) WithBootedVM(t *testing.T, fn func(fw *TestFramework))
WithBootedVM acquires a pooled VM, restores it to a working YANET baseline, runs fn, then returns the VM to the pool.
type HarnessConfig ¶
type HarnessConfig struct {
PoolName string
PoolSize int
BaselineTag string
QEMUImage string
ProjectRoot string
Dataplane string
Controlplane string
Forward string
Route string
EnableSSHForward bool
Prepare func(*TestFramework) error
AfterStart func(*TestFramework) error
ProfileReady func(*TestFramework) error
FingerprintFiles []string
SkipCommonConfig bool
ForceStop bool
}
HarnessConfig describes the VM pool a functional-test package boots.
PoolName names the pool's VM instances and logging scope. BaselineTag scopes the cached baseline template overlay so pools that boot different YANET configurations never collide on one cached snapshot file. QEMUImage, when empty, defaults to the shared functional-test image resolved from the project root. The YAML fields default to the Default*/DataplaneConfig builders when left empty.
type ICMPBuilder ¶
type ICMPBuilder struct {
// contains filtered or unexported fields
}
func ICMP ¶
func ICMP(opts ...ICMPOption) *ICMPBuilder
func (*ICMPBuilder) Build ¶
func (b *ICMPBuilder) Build() gopacket.SerializableLayer
type ICMPOption ¶
type ICMPOption func(*ICMPBuilder)
func ICMPChecksum ¶
func ICMPChecksum(checksum uint16) ICMPOption
func ICMPId ¶
func ICMPId(id uint16) ICMPOption
func ICMPSeq ¶
func ICMPSeq(seq uint16) ICMPOption
func ICMPType ¶
func ICMPType(icmpType layers.ICMPv4TypeCode) ICMPOption
func ICMPTypeCode ¶
func ICMPTypeCode(icmpType uint8, code uint8) ICMPOption
type ICMPv6Builder ¶
type ICMPv6Builder struct {
// contains filtered or unexported fields
}
func ICMPv6 ¶
func ICMPv6(opts ...ICMPv6Option) *ICMPv6Builder
ICMPv6 creates a generic ICMPv6 message. The concrete type/code can be provided via ICMPv6Type / ICMPv6Code options, which is useful for control messages like Router Solicitation that are not modeled as dedicated builders.
func ICMPv6DestUnreach ¶
func ICMPv6DestUnreach(opts ...ICMPv6Option) *ICMPv6Builder
func ICMPv6EchoReply ¶
func ICMPv6EchoReply(opts ...ICMPv6Option) *ICMPv6Builder
func ICMPv6EchoRequest ¶
func ICMPv6EchoRequest(opts ...ICMPv6Option) *ICMPv6Builder
func ICMPv6PacketTooBig ¶
func ICMPv6PacketTooBig(opts ...ICMPv6Option) *ICMPv6Builder
func ICMPv6ParamProblem ¶
func ICMPv6ParamProblem(opts ...ICMPv6Option) *ICMPv6Builder
func ICMPv6TimeExceeded ¶
func ICMPv6TimeExceeded(opts ...ICMPv6Option) *ICMPv6Builder
func (*ICMPv6Builder) Build ¶
func (b *ICMPv6Builder) Build() gopacket.SerializableLayer
type ICMPv6EchoBuilder ¶
type ICMPv6EchoBuilder struct {
// contains filtered or unexported fields
}
ICMPv6EchoBuilder builds an ICMPv6 Echo layer
func ICMPv6Echo ¶
func ICMPv6Echo(opts ...ICMPv6EchoOption) *ICMPv6EchoBuilder
func (*ICMPv6EchoBuilder) Build ¶
func (b *ICMPv6EchoBuilder) Build() gopacket.SerializableLayer
type ICMPv6EchoOption ¶
type ICMPv6EchoOption func(*ICMPv6EchoBuilder)
func ICMPv6EchoId ¶
func ICMPv6EchoId(id uint16) ICMPv6EchoOption
func ICMPv6EchoSeq ¶
func ICMPv6EchoSeq(seq uint16) ICMPv6EchoOption
type ICMPv6NeighborAdvertisementBuilder ¶
type ICMPv6NeighborAdvertisementBuilder struct {
// contains filtered or unexported fields
}
ICMPv6NeighborAdvertisementBuilder builds an ICMPv6 Neighbor Advertisement layer
func ICMPv6NeighborAdvertisement ¶
func ICMPv6NeighborAdvertisement() *ICMPv6NeighborAdvertisementBuilder
func (*ICMPv6NeighborAdvertisementBuilder) Build ¶
func (b *ICMPv6NeighborAdvertisementBuilder) Build() gopacket.SerializableLayer
type ICMPv6NeighborSolicitationBuilder ¶
type ICMPv6NeighborSolicitationBuilder struct {
// contains filtered or unexported fields
}
ICMPv6NeighborSolicitationBuilder builds an ICMPv6 Neighbor Solicitation layer
func ICMPv6NeighborSolicitation ¶
func ICMPv6NeighborSolicitation() *ICMPv6NeighborSolicitationBuilder
func (*ICMPv6NeighborSolicitationBuilder) Build ¶
func (b *ICMPv6NeighborSolicitationBuilder) Build() gopacket.SerializableLayer
type ICMPv6Option ¶
type ICMPv6Option func(*ICMPv6Builder)
func ICMPv6Checksum ¶
func ICMPv6Checksum(checksum uint16) ICMPv6Option
func ICMPv6Code ¶
func ICMPv6Code(code uint8) ICMPv6Option
func ICMPv6Id ¶
func ICMPv6Id(id uint16) ICMPv6Option
func ICMPv6Seq ¶
func ICMPv6Seq(seq uint16) ICMPv6Option
func ICMPv6Type ¶
func ICMPv6Type(typ uint8) ICMPv6Option
ICMPv6Type sets the ICMPv6 type while preserving the current code. This is used for generic control messages (e.g. Router Solicitation) where the type comes directly from the original PCAP.
type ICMPv6RouterAdvertisementBuilder ¶
type ICMPv6RouterAdvertisementBuilder struct {
// contains filtered or unexported fields
}
ICMPv6RouterAdvertisementBuilder builds an ICMPv6 Router Advertisement layer
func ICMPv6RouterAdvertisement ¶
func ICMPv6RouterAdvertisement() *ICMPv6RouterAdvertisementBuilder
func (*ICMPv6RouterAdvertisementBuilder) Build ¶
func (b *ICMPv6RouterAdvertisementBuilder) Build() gopacket.SerializableLayer
type ICMPv6RouterSolicitationBuilder ¶
type ICMPv6RouterSolicitationBuilder struct {
// contains filtered or unexported fields
}
ICMPv6RouterSolicitationBuilder builds an ICMPv6 Router Solicitation layer
func ICMPv6RouterSolicitation ¶
func ICMPv6RouterSolicitation() *ICMPv6RouterSolicitationBuilder
func (*ICMPv6RouterSolicitationBuilder) Build ¶
func (b *ICMPv6RouterSolicitationBuilder) Build() gopacket.SerializableLayer
type IPSecESPBuilder ¶
type IPSecESPBuilder struct {
// contains filtered or unexported fields
}
func IPSecESP ¶
func IPSecESP(opts ...IPSecESPOption) *IPSecESPBuilder
func (*IPSecESPBuilder) Build ¶
func (b *IPSecESPBuilder) Build() gopacket.SerializableLayer
type IPSecESPOption ¶
type IPSecESPOption func(*IPSecESPBuilder)
func ESPEncrypted ¶
func ESPEncrypted(data []byte) IPSecESPOption
func ESPSPI ¶
func ESPSPI(spi uint32) IPSecESPOption
func ESPSeq ¶
func ESPSeq(seq uint32) IPSecESPOption
type IPv4Builder ¶
type IPv4Builder struct {
// contains filtered or unexported fields
}
func IPv4 ¶
func IPv4(opts ...IPv4Option) *IPv4Builder
IPv4 creates an IPv4 layer builder for packet construction
func (*IPv4Builder) Build ¶
func (b *IPv4Builder) Build() gopacket.SerializableLayer
type IPv4Option ¶
type IPv4Option func(*IPv4Builder)
func IPDst ¶
func IPDst(ip string) IPv4Option
func IPFlags ¶
func IPFlags(flags layers.IPv4Flag) IPv4Option
func IPFragOffset ¶
func IPFragOffset(offset uint16) IPv4Option
func IPId ¶
func IPId(id uint16) IPv4Option
func IPProto ¶
func IPProto(proto layers.IPProtocol) IPv4Option
func IPSrc ¶
func IPSrc(ip string) IPv4Option
func IPTOS ¶
func IPTOS(tos uint8) IPv4Option
func IPTTL ¶
func IPTTL(ttl uint8) IPv4Option
func IPv4ChecksumRaw ¶
func IPv4ChecksumRaw(cs uint16) IPv4Option
func IPv4DummyOptions ¶
func IPv4DummyOptions(size int) IPv4Option
func IPv4IHL ¶
func IPv4IHL(ihl uint8) IPv4Option
func IPv4Length ¶
func IPv4Length(length uint16) IPv4Option
func IPv4Options ¶
func IPv4Options(opts []IPv4OptionDef) IPv4Option
IPv4Options sets IPv4 options and automatically updates IHL
func IPv4RawOptions ¶
func IPv4RawOptions(data []byte) IPv4Option
IPv4RawOptions sets raw IPv4 options data (for non-standard options)
type IPv4OptionDef ¶
IPv4OptionDef defines a single IPv4 option
type IPv6Builder ¶
type IPv6Builder struct {
// contains filtered or unexported fields
}
func IPv6 ¶
func IPv6(opts ...IPv6Option) *IPv6Builder
func (*IPv6Builder) Build ¶
func (b *IPv6Builder) Build() gopacket.SerializableLayer
type IPv6ExtHdrDestOptBuilder ¶
type IPv6ExtHdrDestOptBuilder struct {
// contains filtered or unexported fields
}
IPv6ExtHdrDestOptBuilder builds IPv6 Destination Options extension header
func IPv6ExtHdrDestOpt ¶
func IPv6ExtHdrDestOpt(opts ...IPv6ExtHdrDestOptOption) *IPv6ExtHdrDestOptBuilder
IPv6ExtHdrDestOpt creates a new IPv6 Destination Options extension header builder
func (*IPv6ExtHdrDestOptBuilder) Build ¶
func (b *IPv6ExtHdrDestOptBuilder) Build() gopacket.SerializableLayer
Build constructs the IPv6 Destination Options extension header
type IPv6ExtHdrDestOptOption ¶
type IPv6ExtHdrDestOptOption func(*IPv6ExtHdrDestOptBuilder)
IPv6ExtHdrDestOptOption is an option for IPv6ExtHdrDestOpt
func IPv6DestOptAddOption ¶
func IPv6DestOptAddOption(optType uint8, data []byte) IPv6ExtHdrDestOptOption
IPv6DestOptAddOption adds an option to the destination options header
func IPv6DestOptNextHeader ¶
func IPv6DestOptNextHeader(nh layers.IPProtocol) IPv6ExtHdrDestOptOption
IPv6DestOptNextHeader sets the next header field (for compatibility, but not used in gopacket) The next header is automatically determined during serialization
type IPv6FragmentBuilder ¶
type IPv6FragmentBuilder struct {
// contains filtered or unexported fields
}
func IPv6ExtHdrFragment ¶
func IPv6ExtHdrFragment(opts ...IPv6FragmentOption) *IPv6FragmentBuilder
func (*IPv6FragmentBuilder) Build ¶
func (b *IPv6FragmentBuilder) Build() gopacket.SerializableLayer
type IPv6FragmentOption ¶
type IPv6FragmentOption func(*IPv6FragmentBuilder)
func IPv6FragId ¶
func IPv6FragId(id uint32) IPv6FragmentOption
func IPv6FragM ¶
func IPv6FragM(m bool) IPv6FragmentOption
func IPv6FragNextHeader ¶
func IPv6FragNextHeader(nh layers.IPProtocol) IPv6FragmentOption
func IPv6FragOffset ¶
func IPv6FragOffset(offset uint16) IPv6FragmentOption
type IPv6Option ¶
type IPv6Option func(*IPv6Builder)
func IPv6Dst ¶
func IPv6Dst(ip string) IPv6Option
func IPv6FlowLabel ¶
func IPv6FlowLabel(fl uint32) IPv6Option
func IPv6HopLimit ¶
func IPv6HopLimit(hl uint8) IPv6Option
func IPv6NextHeader ¶
func IPv6NextHeader(nh layers.IPProtocol) IPv6Option
func IPv6PayloadLength ¶
func IPv6PayloadLength(plen uint16) IPv6Option
func IPv6Src ¶
func IPv6Src(ip string) IPv6Option
func IPv6TrafficClass ¶
func IPv6TrafficClass(tc uint8) IPv6Option
type LayerBuilder ¶
type LayerBuilder interface {
Build() gopacket.SerializableLayer
}
LayerBuilder interface for all layer builders
type MPLSBuilder ¶
type MPLSBuilder struct {
// contains filtered or unexported fields
}
MPLSBuilder builds MPLS layer
func (*MPLSBuilder) Build ¶
func (b *MPLSBuilder) Build() gopacket.SerializableLayer
Build constructs the MPLS layer
type MPLSOption ¶
type MPLSOption func(*MPLSBuilder)
MPLSOption is an option for MPLS
func MPLSStackBit ¶
func MPLSStackBit(stackBit bool) MPLSOption
MPLSStackBit sets the bottom-of-stack bit
func MPLSTrafficClass ¶
func MPLSTrafficClass(tc uint8) MPLSOption
MPLSTrafficClass sets the traffic class (experimental bits)
type PacketInfo ¶
type PacketInfo struct {
// Ethernet layer addressing information
SrcMAC net.HardwareAddr // Source MAC address from Ethernet header
DstMAC net.HardwareAddr // Destination MAC address from Ethernet header
// IP layer information (supports both IPv4 and IPv6)
IsIPv4 bool // True if packet contains IPv4 header
IsIPv6 bool // True if packet contains IPv6 header
SrcIP net.IP // Source IP address (IPv4 or IPv6)
DstIP net.IP // Destination IP address (IPv4 or IPv6)
Protocol layers.IPProtocol // IPv4 protocol field
NextHeader layers.IPProtocol // IPv6 next header field
// Transport layer information
SrcPort uint16 // Source port number (TCP/UDP)
DstPort uint16 // Destination port number (TCP/UDP)
// Tunnel detection and analysis
IsTunneled bool // True if packet is tunneled/encapsulated
TunnelType string // Tunnel type: "ip4in4", "ip6in4", "ip4in6", "ip6in6", "gre"
InnerPacket *PacketInfo // Parsed information about the inner/encapsulated packet
// Raw packet data
RawData []byte // Complete raw packet bytes including all headers
Payload []byte // Application layer payload data
}
PacketInfo contains comprehensive parsed information about a network packet, including all protocol layers from Ethernet to application data. It supports complex scenarios such as tunneled packets, fragmentation, and various encapsulation protocols commonly used in network testing.
The structure provides detailed information about:
- Ethernet layer addressing (MAC addresses)
- IP layer information (IPv4/IPv6 addressing and protocols)
- Transport layer details (TCP/UDP port information)
- Tunnel detection and inner packet parsing
- Raw packet data and payload extraction
func (*PacketInfo) GetTransportProtocol ¶
func (info *PacketInfo) GetTransportProtocol() (layers.IPProtocol, bool)
GetTransportProtocol returns the transport layer protocol (TCP/UDP) from the packet. For tunneled packets, it returns the protocol from the inner packet. Returns the IPProtocol value and a boolean indicating if a valid transport protocol was found.
func (*PacketInfo) String ¶
func (info *PacketInfo) String() string
String returns a human-readable string representation of the packet information, including all relevant protocol details, addressing information, and tunnel status. This method is useful for debugging, logging, and test result presentation.
The string representation includes:
- Source and destination IP addresses
- IP version information (IPv4/IPv6) with protocol details
- Transport layer port information (if available)
- Tunnel information and inner packet details (if tunneled)
Returns:
- string: Formatted packet information suitable for display and logging
Example output:
"Packet: 192.168.1.1 -> 10.0.0.1 (IPv4, proto=4), tunnel=ip4in4, inner=172.16.1.1->172.16.1.2"
type PacketParser ¶
type PacketParser struct{}
PacketParser provides comprehensive packet parsing and verification functionality for network testing scenarios. It supports complex packet analysis including tunnel detection, protocol verification, and NAT64 translation validation.
The parser handles various networking protocols and encapsulation methods commonly encountered in YANET testing environments.
func NewPacketParser ¶
func NewPacketParser() *PacketParser
NewPacketParser creates a new packet parser instance ready for packet analysis. The parser is stateless and can be safely used concurrently across multiple goroutines for parallel packet processing.
Returns:
- *PacketParser: A new packet parser instance
func (*PacketParser) ParsePacket ¶
func (p *PacketParser) ParsePacket(data []byte) (*PacketInfo, error)
ParsePacket parses raw packet data and extracts comprehensive protocol information into a structured PacketInfo object. The method handles various networking protocols and automatically detects tunneled/encapsulated packets.
The parsing process includes:
- Ethernet frame validation and padding to minimum size
- Multi-layer protocol parsing (Ethernet, IP, Transport)
- Tunnel detection and inner packet analysis
- Payload extraction and error handling
Parameters:
- data: Raw packet bytes to parse (minimum 60 bytes after padding)
Returns:
- *PacketInfo: Structured packet information with all parsed layers
- error: An error if packet parsing fails or data is malformed
Example:
parser := NewPacketParser()
info, err := parser.ParsePacket(rawPacketData)
if err != nil {
log.Fatalf("Packet parsing failed: %v", err)
}
fmt.Printf("Parsed packet: %s", info.String())
func (*PacketParser) VerifyDecapsulation ¶
func (p *PacketParser) VerifyDecapsulation(originalPacket, processedPacket *PacketInfo) error
VerifyDecapsulation validates that a tunneled packet has been properly decapsulated by comparing the processed packet against the original inner packet. This verification method is essential for testing tunnel processing functionality.
The verification process includes:
- Confirming the original packet was tunneled
- Validating inner packet information exists
- Comparing IP version consistency (IPv4/IPv6)
- Verifying source and destination IP address preservation
- Ensuring tunnel headers have been properly removed
Parameters:
- originalPacket: The original tunneled packet before processing
- processedPacket: The packet after decapsulation processing
Returns:
- error: An error if decapsulation verification fails or packets don't match
Example:
err := parser.VerifyDecapsulation(tunneled, decapsulated)
if err != nil {
log.Fatalf("Decapsulation verification failed: %v", err)
}
func (*PacketParser) VerifyNAT64Translation ¶
func (p *PacketParser) VerifyNAT64Translation(originalPacket, translatedPacket *PacketInfo, nat64Prefix string) error
VerifyNAT64Translation validates NAT64 protocol translation between IPv4 and IPv6 packets, ensuring proper address mapping and prefix usage. This method is crucial for testing NAT64 functionality in dual-stack network environments.
The verification process includes:
- IPv4-to-IPv6 translation validation with prefix checking
- IPv6-to-IPv4 translation validation with embedded address extraction
- NAT64 prefix compliance verification (typically 64:ff9b::/96)
- Embedded IPv4 address consistency checking
Supported translation directions:
- IPv4 → IPv6: Verifies IPv6 address is in NAT64 prefix with embedded IPv4
- IPv6 → IPv4: Verifies original IPv6 was in prefix and extracts embedded IPv4
Parameters:
- originalPacket: The packet before NAT64 translation
- translatedPacket: The packet after NAT64 translation
- nat64Prefix: NAT64 prefix in CIDR notation (e.g., "64:ff9b::/96")
Returns:
- error: An error if NAT64 translation verification fails or addresses don't match
Example:
err := parser.VerifyNAT64Translation(ipv4Pkt, ipv6Pkt, "64:ff9b::/96")
if err != nil {
log.Fatalf("NAT64 translation verification failed: %v", err)
}
type QEMUManager ¶
type QEMUManager struct {
Name string
ImagePath string
WorkDir string
Command *exec.Cmd
LogsDir string
ConfigDir string
BuildDir string
TargetDir string
ProjectDir string
SerialPath string
MonitorPath string
SocketPaths []string
Ninepmounted atomic.Bool
// TemplateOverlay is an optional path to a qcow2 overlay that already
// contains a reusable VM snapshot. When set, Start() copies it instead
// of creating a blank overlay, then boots with -loadvm TemplateSnapshotName.
TemplateOverlay string
// TemplateSnapshotName is the snapshot name loaded from TemplateOverlay.
// When empty, Start() falls back to BootedSnapshotName.
TemplateSnapshotName string
// contains filtered or unexported fields
}
QEMUManager handles the complete lifecycle and operations of QEMU virtual machines for YANET functional testing. It provides comprehensive VM management including startup, networking configuration, filesystem sharing, and graceful shutdown.
The manager supports:
- QEMU VM lifecycle management with proper resource cleanup
- Unix socket-based networking for packet injection and capture
- 9P filesystem sharing for host-VM file exchange
- Serial console and monitor interface access
- VM readiness detection and synchronization
- Parallel test execution with unique instance isolation
All operations are thread-safe and support concurrent access patterns required for comprehensive network testing scenarios.
func NewQEMUManager ¶
func NewQEMUManager(name string, imagePath string, logger *zap.SugaredLogger) (*QEMUManager, error)
NewQEMUManager creates and initializes a new QEMU manager instance for virtual machine testing. The manager sets up all necessary directories, generates unique instance identifiers for parallel execution, and configures filesystem sharing paths for host-VM communication.
The initialization process includes:
- Unique instance ID generation for parallel test isolation
- Working directory creation in system temporary space
- Project root detection for build and target directory sharing
- Socket path configuration for VM networking and console access
Parameters:
- name: Name of this manager
- imagePath: Path to the QEMU disk image file (must exist and be accessible)
- logger: Structured logger for debugging and monitoring VM operations
Returns:
- *QEMUManager: Configured QEMU manager ready for VM startup
- error: An error if project root detection fails or paths are invalid
Example:
manager, err := NewQEMUManager("main", "/path/to/vm-image.qcow2", logger)
if err != nil {
log.Fatalf("Failed to create QEMU manager: %v", err)
}
func (*QEMUManager) AbortSerial ¶
func (q *QEMUManager) AbortSerial()
AbortSerial stops the serial reader and closes the serial connection, causing in-flight ExecuteCommand calls to fail immediately. The caller must reconnect (via reset/up) before issuing further guest commands.
func (*QEMUManager) AttachSerial ¶
func (q *QEMUManager) AttachSerial() (net.Conn, func() error, error)
AttachSerial gives a caller exclusive access to the serial console.
The returned release function restores the framework's serial reader and waits for a fresh prompt before another framed command can run.
func (*QEMUManager) EnableSSHForward ¶
func (q *QEMUManager) EnableSSHForward()
EnableSSHForward configures a loopback SSH forward for this VM.
func (*QEMUManager) ForceStop ¶
func (q *QEMUManager) ForceStop()
ForceStop makes Stop terminate this VM even when the test keep-alive mode is set.
func (*QEMUManager) GetStdin ¶
func (q *QEMUManager) GetStdin() io.WriteCloser
GetStdin returns the stdin pipe for the QEMU process
func (*QEMUManager) IsVMReady ¶
func (q *QEMUManager) IsVMReady() bool
IsVMReady returns the current readiness state of the virtual machine in a thread-safe manner. This method can be called from multiple goroutines without synchronization concerns.
VM readiness indicates that the virtual machine has completed its boot process and is ready to accept and execute commands through the serial console.
Returns:
- bool: True if the VM is ready for command execution, false otherwise
Example:
if manager.IsVMReady() {
// Safe to execute commands
output, err := cli.ExecuteCommand("ls -la")
}
func (*QEMUManager) ReconnectSerial ¶
func (q *QEMUManager) ReconnectSerial() error
ReconnectSerial closes the current serial connection and opens a new one. The caller is responsible for resetting readySignal and launching a new readSerial goroutine after this returns.
func (*QEMUManager) RestartSerial ¶
func (q *QEMUManager) RestartSerial() error
RestartSerial reconnects the serial console and restarts the reader goroutine after an AbortSerial call. Use this to restore command execution capability after an abort.
func (*QEMUManager) RestoreBooted ¶
func (q *QEMUManager) RestoreBooted() error
RestoreBooted restores the VM to the "booted" snapshot without going through the full framework-level RestoreAndReconnect. It handles the monitor loadvm, serial reconnect, and ready wait at the QEMUManager level.
Callers are responsible for unmounting 9P before calling and remounting after.
func (*QEMUManager) RestoreSnapshot ¶
func (q *QEMUManager) RestoreSnapshot(name string) error
RestoreSnapshot restores the VM to a previously saved snapshot. After restore, the serial console connection is broken (the guest-side state reverts) and must be re-established via ReconnectSerial.
The monitor connection itself survives because it is external to the guest state.
func (*QEMUManager) SSHPort ¶
func (q *QEMUManager) SSHPort() int
SSHPort returns the loopback port forwarded to the guest SSH server.
func (*QEMUManager) SaveBootedOverlay ¶
func (q *QEMUManager) SaveBootedOverlay() (string, error)
SaveBootedOverlay saves the "booted" snapshot to the VM's current overlay and returns the overlay file path. The caller can copy this path to a cache location and use it as TemplateOverlay for other VMs.
func (*QEMUManager) SaveSnapshot ¶
func (q *QEMUManager) SaveSnapshot(name string) error
SaveSnapshot creates a named QEMU VM snapshot via the monitor. The snapshot captures the full VM state (CPU, RAM, devices) and can be restored later with RestoreSnapshot. This is the key primitive for per-test state isolation.
func (*QEMUManager) SaveSnapshotOverlay ¶
func (q *QEMUManager) SaveSnapshotOverlay(name string) (string, error)
SaveSnapshotOverlay saves the named snapshot to the VM's current overlay and returns the overlay file path. The caller can copy this path to a cache location and use it as TemplateOverlay for other VMs.
The 9P shares must be unmounted before calling (savevm blocks when VirtFS mounts are active).
func (*QEMUManager) SendMonitorCommand ¶
func (q *QEMUManager) SendMonitorCommand(cmd string) (string, error)
SendMonitorCommand sends a command to the QEMU monitor (HMP) and returns the response. The monitor socket must already be connected via Start(). Commands are terminated with a newline; the method waits for the next "(qemu) " prompt to determine when the response is complete.
func (*QEMUManager) Start ¶
func (q *QEMUManager) Start() (bool, error)
Start launches a QEMU virtual machine. When TemplateOverlay is set the overlay is copied from it and QEMU starts with -loadvm for the requested TemplateSnapshotName, skipping the slow cold boot path.
Returns (true, nil) when booted from snapshot, (false, nil) when doing a full cold boot.
func (*QEMUManager) Stop ¶
func (q *QEMUManager) Stop() error
Stop performs graceful termination of the QEMU virtual machine and comprehensive cleanup of all associated resources. This method ensures proper resource deallocation and prevents resource leaks in testing environments.
The cleanup process includes:
- Graceful closure of monitor and serial console connections
- QEMU process termination with proper signal handling
- Working directory and temporary file cleanup
- Unix socket file removal from filesystem
- Error collection and reporting for failed cleanup operations
Multiple cleanup errors are collected and returned as a combined error to provide comprehensive information about any cleanup failures.
Returns:
- error: A combined error if any cleanup operations fail, or nil if successful
Example:
if err := manager.Stop(); err != nil {
log.Errorf("VM cleanup encountered errors: %v", err)
}
func (*QEMUManager) WaitForReady ¶
func (q *QEMUManager) WaitForReady(timeout time.Duration) error
WaitForReady blocks until the virtual machine becomes ready for command execution or the specified timeout expires. This method provides synchronous waiting for VM readiness with proper timeout handling.
The method first checks if the VM is already ready to avoid unnecessary waiting. If not ready, it waits for the readiness signal from the background monitoring goroutine.
Parameters:
- timeout: Maximum time to wait for VM readiness
Returns:
- error: An error if the timeout expires before VM becomes ready, or nil if ready
Example:
if err := manager.WaitForReady(60 * time.Second); err != nil {
log.Fatalf("VM failed to become ready: %v", err)
}
type RawBuilder ¶
type RawBuilder struct {
// contains filtered or unexported fields
}
func Raw ¶
func Raw(data []byte) *RawBuilder
func (*RawBuilder) Build ¶
func (b *RawBuilder) Build() gopacket.SerializableLayer
type ReplyBudget ¶
type ReplyBudget struct {
// contains filtered or unexported fields
}
ReplyBudget bounds how many reads one subtest will wait out for replies that never arrive.
A caller creates one ReplyBudget per subtest and passes it to every SendAndReceivePacket call in that subtest. A reply that arrives costs the pool nothing, because only a timed-out read debits it. Once drained, later reads still wait at exhaustedReadTimeout rather than skipping outright, so a reply that arrives promptly is still collected. The debit is a flat per-read cap, not an elapsed-time one, so a single read can still exceed it.
func NewReplyBudget ¶
func NewReplyBudget() *ReplyBudget
NewReplyBudget returns a ReplyBudget with a fresh pool for one subtest.
type SocketClient ¶
type SocketClient struct {
// contains filtered or unexported fields
}
SocketClient handles bidirectional communication with QEMU virtual machine network interfaces through socket connections. It supports both TCP and Unix socket connections for packet injection and capture in testing scenarios.
The client provides:
- Reliable packet transmission with length prefixing
- Packet reception with filtering and timeout handling
- Connection management with automatic retry logic
- MAC address-based packet filtering for test isolation
- Configurable timeouts and logging for debugging
All network operations include proper error handling and timeout management to ensure robust testing in various network conditions.
func NewSocketClient ¶
func NewSocketClient(socketPath string, opts ...SocketClientOption) (*SocketClient, error)
NewSocketClient creates a new socket client configured for Unix socket connections to QEMU network interfaces. This method provides the primary interface for packet injection and capture through Unix domain sockets.
Unix socket connections offer better performance and lower latency compared to TCP sockets, making them ideal for high-throughput network testing scenarios.
Parameters:
- socketPath: Path to the Unix socket file created by QEMU
- opts: Optional functional options for client customization
Returns:
- *SocketClient: Configured socket client ready for Unix socket connections
- error: An error if the socket path is empty or options cannot be applied
Example:
client, err := NewSocketClient("/tmp/qemu-socket.sock", SocketClientWithLog(logger))
if err != nil {
log.Fatalf("Failed to create Unix socket client: %v", err)
}
func NewSocketClientTCP ¶
func NewSocketClientTCP(port int, opts ...SocketClientOption) (*SocketClient, error)
NewSocketClientTCP creates a new socket client configured for TCP connections to QEMU network interfaces. This method is used when QEMU is configured with TCP socket networking for packet injection and capture.
The client is initialized with default settings and can be customized using functional options for timeout configuration and logging setup.
Parameters:
- port: TCP port number for QEMU socket connection (1-65535)
- opts: Optional functional options for client customization
Returns:
- *SocketClient: Configured socket client ready for TCP connections
- error: An error if the port number is invalid or options cannot be applied
Example:
client, err := NewSocketClientTCP(8080, WithTimeout(10*time.Second))
if err != nil {
log.Fatalf("Failed to create TCP socket client: %v", err)
}
func (*SocketClient) Close ¶
func (sc *SocketClient) Close() error
Close gracefully terminates the socket connection and releases associated network resources. This method should be called when the socket client is no longer needed to prevent resource leaks.
The method safely handles cases where no connection is established and provides proper error reporting for connection closure failures.
Returns:
- error: An error if connection closure fails, or nil if successful or no connection exists
Example:
defer func() {
if err := client.Close(); err != nil {
log.Errorf("Failed to close socket connection: %v", err)
}
}()
func (*SocketClient) Connect ¶
func (sc *SocketClient) Connect() error
Connect establishes a network connection to the QEMU socket interface with automatic retry logic and proper error handling. The method supports both TCP and Unix socket connections based on the client configuration.
The connection process includes:
- Automatic detection of connection type (TCP vs Unix socket)
- Retry logic with exponential backoff for handling timing issues
- Proper error reporting and logging for debugging
- Connection state management to prevent duplicate connections
The method attempts connection up to 10 times with 2-second intervals to accommodate QEMU startup timing and network interface availability.
Returns:
- error: An error if connection cannot be established after all retry attempts
Example:
if err := client.Connect(); err != nil {
log.Fatalf("Failed to connect to QEMU socket: %v", err)
}
func (*SocketClient) GetSocketPort ¶
func (sc *SocketClient) GetSocketPort() int
GetSocketPort returns the TCP port number configured for this socket client. This method is useful for debugging and logging purposes, particularly when working with multiple socket clients on different ports.
Returns:
- int: TCP port number, or 0 if the client is configured for Unix sockets
Example:
port := client.GetSocketPort()
if port > 0 {
log.Infof("Client configured for TCP port %d", port)
} else {
log.Info("Client configured for Unix socket")
}
func (*SocketClient) ReceiveAllPackets ¶
ReceiveAllPackets receives all packets available on the socket within the timeout.
func (*SocketClient) ReceiveAllPacketsUnfiltered ¶
func (sc *SocketClient) ReceiveAllPacketsUnfiltered(timeout time.Duration, dumpPath string) ([][]byte, error)
ReceiveAllPacketsUnfiltered receives every packet observed on the socket during a fixed window of timeout, without the destination-MAC filtering ReceiveAllPackets applies.
Solicited responses to injected traffic always carry the host's simulated MAC as destination, which is what that filter matches. Traffic the dataplane emits on its own initiative often addresses a multicast destination instead and would otherwise be silently dropped, so callers that need to observe unsolicited traffic must use this method.
Unlike ReceiveAllPackets, which stops as soon as the link falls idle for timeout, this method always reads for the entire window: a source that emits periodic unsolicited traffic more often than timeout never falls idle, so an idle-based cutoff would never return.
func (*SocketClient) ReceivePacket ¶
ReceivePacket captures a network packet from the QEMU socket connection with MAC address filtering and timeout handling. The method implements the QEMU socket protocol by reading length-prefixed packets and filtering based on destination MAC address for test isolation.
The reception process includes:
- Connection state validation and timeout configuration
- Length prefix parsing from network byte order
- Packet size validation to prevent buffer overflows
- Complete packet data reception
- MAC address-based filtering for test packet isolation
- Automatic packet parsing and validation
- Optional dumping to file if dumpFilePaths is provided
The method continuously reads packets until it finds one with the correct destination MAC address matching the test framework's source MAC, ensuring proper packet isolation in multi-test scenarios.
Parameters:
- timeout: Maximum time to wait for packet reception
- dumpPath: Path to dump file for recording socket data (empty string to skip dumping)
Returns:
- []byte: Raw packet data with correct destination MAC address
- error: An error if connection fails, timeout occurs, or no matching packet is received
Example:
packet, err := client.ReceivePacket(5 * time.Second, "/path/to/dump.file")
if err != nil {
log.Fatalf("Packet reception failed: %v", err)
}
fmt.Printf("Received packet: %x", packet)
func (*SocketClient) ResetConnection ¶
func (sc *SocketClient) ResetConnection() error
ResetConnection closes the existing connection and creates a new one. This ensures a clean stream with no buffered data from previous operations. This method is used for test isolation to prevent packet leakage between tests.
The method:
- Closes any existing connection (discarding buffered data)
- Creates a new connection to the same socket
- Returns an error if reconnection fails
Returns:
- error: An error if reconnection fails, or nil if successful
Example:
if err := client.ResetConnection(); err != nil {
log.Fatalf("Failed to reset connection: %v", err)
}
func (*SocketClient) SendAndReceivePacket ¶
func (sc *SocketClient) SendAndReceivePacket(budget *ReplyBudget, packet []byte, dumpPath string) ([]byte, error)
SendAndReceivePacket transmits packet and waits for a single reply, returning the raw reply bytes.
The wait is not retried on timeout: a late reply is far more likely than a lost one under a starved CI runner, and resending risks a duplicate packet through stateful modules. budget must be shared by every call in one subtest — see ReplyBudget for what that buys when replies are missing by construction. A missing reply returns a nil slice and a nil error, not an error, and surfaces later as a packet-count mismatch.
Parameters:
- budget: Per-subtest pool debited only when a read times out
- packet: Raw packet bytes to transmit through the socket
- dumpPath: Path to dump file for recording socket data (empty string to skip dumping)
Returns:
- []byte: Raw reply bytes, or nil if no reply arrived
- error: An error if the packet could not be sent, or the read failed for a reason other than a timeout
func (*SocketClient) SendPacket ¶
func (sc *SocketClient) SendPacket(packet []byte, dumpPath string) error
SendPacket transmits a raw network packet through the QEMU socket connection with proper length prefixing and timeout handling. The method implements the QEMU socket protocol by prefixing each packet with its length in network byte order.
The transmission process includes:
- Connection state validation
- Write timeout configuration for reliable transmission
- Length prefix encoding in big-endian format
- Complete packet data transmission
- Comprehensive error handling and logging
- Optional dumping to file if dumpFilePaths is provided
The packet format follows QEMU socket networking protocol:
[4-byte length][packet data]
Parameters:
- packet: Raw packet bytes to transmit through the socket
- dumpPath: Path to dump file for recording socket data (empty string to skip dumping)
Returns:
- error: An error if the connection is not established, timeout occurs, or transmission fails
Example:
packetData := []byte{0x52, 0x54, 0x00, 0x6b, 0xff, 0xa1, ...} // Ethernet frame
if err := client.SendPacket(packetData, "/path/to/dump.file"); err != nil {
log.Fatalf("Packet transmission failed: %v", err)
}
func (*SocketClient) SendPackets ¶
func (sc *SocketClient) SendPackets(packets [][]byte, dumpPath string) error
func (*SocketClient) WithLog ¶
func (sc *SocketClient) WithLog(log *zap.SugaredLogger) *SocketClient
WithLog creates a new SocketClient instance with a different logger while sharing the same underlying connection (inner state). This allows each test to have its own logging context while sharing the socket connection.
Parameters:
- log: Logger to use for this client instance
Returns:
- *SocketClient: A new socket client instance with the specified logger
Example:
namedClient := client.WithLog(logger.Named("test1"))
type SocketClientOption ¶
type SocketClientOption func(*SocketClient) error
SocketClientOption defines functional options for configuring SocketClient instances. This pattern allows flexible initialization with optional parameters while maintaining clean API design and backward compatibility.
func WithTimeout ¶
func WithTimeout(timeout time.Duration) SocketClientOption
WithTimeout configures the default timeout for socket read and write operations. This timeout applies to both packet transmission and reception operations, providing consistent behavior across all network interactions.
Parameters:
- timeout: Duration for socket operation timeouts (must be positive)
Returns:
- SocketClientOption: Functional option that sets the timeout
- error: An error if the timeout value is invalid (zero or negative)
Example:
client, err := NewSocketClient(path, WithTimeout(5*time.Second))
type TCPBuilder ¶
type TCPBuilder struct {
// contains filtered or unexported fields
}
func TCP ¶
func TCP(opts ...TCPOption) *TCPBuilder
func (*TCPBuilder) Build ¶
func (b *TCPBuilder) Build() gopacket.SerializableLayer
type TCPOption ¶
type TCPOption func(*TCPBuilder)
func TCPChecksumRaw ¶
func TCPDataOffset ¶
func TCPDummyOptions ¶
func TCPOptions ¶
func TCPOptions(opts []TCPOptionDef) TCPOption
TCPOptions sets TCP options and automatically updates DataOffset
type TCPOptionDef ¶
type TCPOptionDef struct {
Kind layers.TCPOptionKind
Length uint8
Data []byte
}
TCPOptionDef defines a single TCP option
type TCPPacketOpts ¶
type TCPPacketOpts struct {
SrcMAC string // default: framework.SrcMAC
DstMAC string // default: framework.DstMAC
SrcPort uint16 // default: 12345
DstPort uint16 // default: 80
SYN bool // default: false
ACK bool // default: true
PSH bool // default: true
Seq uint32 // default: 1
Ack uint32 // default: 1
Window uint16 // default: 1024
}
TCPPacketOpts configures optional fields for CreateTCPIPv4Packet and CreateTCPIPv6Packet. Zero values use sensible defaults matching the most common test pattern.
type TestFramework ¶
type TestFramework struct {
PacketParser *PacketParser
Paths GuestPaths // Guest-side filesystem paths (9P or local tmpfs)
// contains filtered or unexported fields
}
F represents the main test framework structure for YANET functional testing. It orchestrates QEMU virtual machine management, CLI command execution, packet processing, and network socket communication to provide a comprehensive testing environment.
The framework manages:
- QEMU virtual machine lifecycle and networking
- CLI command execution within the VM
- Packet parsing and analysis capabilities
- Socket-based network communication with VM interfaces
- Test context via optional *testing.T field
All operations are thread-safe through internal synchronization mechanisms. This type has private fields and cannot be constructed directly - use Global() and ForTest(t).
func (*TestFramework) AbortGuestSerial ¶
func (f *TestFramework) AbortGuestSerial()
AbortGuestSerial closes the guest serial connection so in-flight ExecuteCommand calls fail. The caller must reconnect before issuing further guest commands.
func (*TestFramework) AdoptRunningConfig ¶
func (f *TestFramework) AdoptRunningConfig(dataplaneConfig string, controlplaneConfig string)
AdoptRunningConfig records the configuration to restart the guest with.
It starts nothing. A VM restored from a baseline snapshot boots with YANET already running, so the framework driving it never called StartYANET and therefore holds no configuration for RestartYANET to restart from. Call this once the caller knows what configuration the running guest was baked with, so a later in-place restart has something to fall back to.
func (*TestFramework) AttachSerial ¶
func (f *TestFramework) AttachSerial() (net.Conn, func() error, error)
AttachSerial gives a caller exclusive access to the guest serial console.
func (*TestFramework) CommonConfigCommands ¶
func (f *TestFramework) CommonConfigCommands() []string
CommonConfigCommands returns the shell commands that configure the baseline YANET state (kni0, forwarding, route FIB, pipelines, devices). Paths are resolved from f.Paths so the commands work with both 9P and local tmpfs layouts.
func (*TestFramework) CreateConfigFile ¶
func (f *TestFramework) CreateConfigFile(name string, config string) error
func (*TestFramework) CreateForwardConfig ¶
func (f *TestFramework) CreateForwardConfig(config string) error
CreateForwardConfig writes forward.yaml to the path referenced by f.Paths.ForwardYAML. In 9P mode this writes to the host filesystem; in local mode it writes via serial console to the guest tmpfs.
func (*TestFramework) ExecuteCommand ¶
func (f *TestFramework) ExecuteCommand(command string) (string, error)
ExecuteCommand executes a single CLI command within the QEMU virtual machine via the serial console interface. This is a proxy method that delegates to the underlying CLI manager.
For test-specific frameworks (created via ForTest), commands are logged with the test's unique log ID for easy debugging.
Parameters:
- command: The shell command to execute in the virtual machine
Returns:
- string: The cleaned command output (stdout/stderr combined)
- error: An error if the VM is not ready, command fails, or timeout occurs
Example:
fw := globalFramework.ForTest(t)
fw.Run("ExecuteCommand", func(fw *TestFramework, t *testing.T) {
output, err := fw.ExecuteCommand("ls -la /etc")
require.NoError(t, err)
t.Logf("Output: %s", output)
})
func (*TestFramework) ExecuteCommandWithTimeout ¶
func (f *TestFramework) ExecuteCommandWithTimeout(command string, timeout time.Duration) (string, error)
ExecuteCommandWithTimeout executes a single CLI command with a custom timeout. Use this for operations that may take longer than the default 30s, such as copying large binaries on slow emulated VMs.
func (*TestFramework) ExecuteCommands ¶
func (f *TestFramework) ExecuteCommands(commands ...string) ([]string, error)
ExecuteCommands executes multiple CLI commands sequentially within the QEMU virtual machine. This is a proxy method that delegates to the underlying CLI manager.
Each command is executed in order, and execution stops at the first command that returns an error.
Parameters:
- commands: Variable number of shell commands to execute sequentially
Returns:
- []string: Slice of command outputs in execution order (may be partial if error occurs)
- error: An error from the first failed command, or nil if all commands succeed
Example:
fw := globalFramework.ForTest(t)
fw.Run("ExecuteCommands", func(fw *TestFramework, t *testing.T) {
outputs, err := fw.ExecuteCommands(
"mkdir -p /tmp/test",
"echo 'hello' > /tmp/test/file.txt",
"cat /tmp/test/file.txt",
)
require.NoError(t, err)
t.Logf("Outputs: %v", outputs)
})
func (*TestFramework) ExecuteCommandsSeparately ¶
func (f *TestFramework) ExecuteCommandsSeparately(commands ...string) ([]string, []error)
ExecuteCommandsSeparately runs every command to completion regardless of individual failures, pairing each output with its own error.
func (*TestFramework) ExportCurrentOverlay ¶
func (f *TestFramework) ExportCurrentOverlay(dst string) error
ExportCurrentOverlay copies the VM's current qcow2 overlay to dst.
A successful export leaves the VM paused. Callers must stop it or explicitly resume it before further use. This is used to cache prepared template overlays and start future pool VMs from the same snapshot source.
func (*TestFramework) ForTest ¶
func (f *TestFramework) ForTest(t *testing.T) *TestFramework
ForTest binds an already constructed framework instance to a specific test. This is used by pooled VMs, where tests acquire a shared base framework from VMPool and then need a test-scoped copy with the proper logger and *testing.T.
func (*TestFramework) GetSocketClient ¶
func (f *TestFramework) GetSocketClient(ifaceIndex int) (*SocketClient, error)
GetSocketClient retrieves or creates a socket client for the specified network interface. The method implements caching to reuse existing connections and ensures thread-safe access to the socket client pool.
For test-specific frameworks (created via ForTest), the socket client is returned with a named logger using the test's log ID.
The method handles:
- Interface index validation against available QEMU socket paths
- Thread-safe access to the socket client cache
- Automatic socket client creation for new interfaces
- Client caching for performance optimization
- Logger customization for test-specific contexts
Parameters:
- ifaceIndex: Zero-based index of the network interface (must be < len(SocketPaths))
Returns:
- *SocketClient: Socket client for the specified interface
- error: An error if the interface index is invalid or client creation fails
Example:
client, err := fw.GetSocketClient(0)
if err != nil {
log.Fatalf("Failed to get socket client: %v", err)
}
defer client.Close()
func (*TestFramework) GetSocketPaths ¶
func (f *TestFramework) GetSocketPaths() []string
func (*TestFramework) Mount9P ¶
func (f *TestFramework) Mount9P() error
Mount9P remounts all 9P shares inside the guest VM. Uses mount -a for speed, then verifies each mount point is present.
func (*TestFramework) PrepareLocalStorage ¶
func (f *TestFramework) PrepareLocalStorage() error
PrepareLocalStorage copies all YANET binaries, CLI tools, configs and scripts from 9P mounts to /tmp/yanet/ inside the guest. After this call, YANET can be started from local tmpfs paths so that no process holds open fids on 9P mounts, making savevm/loadvm work cleanly.
This also switches f.Paths to LocalGuestPaths().
func (*TestFramework) ResetConnections ¶
func (f *TestFramework) ResetConnections()
ResetConnections closes and reconnects all socket clients. This ensures a clean state after a snapshot restore, preventing stale connections from causing false heartbeat failures. Unlike draining, this approach guarantees a completely clean stream by discarding any buffered data with the old connection.
Must be called after loadvm and before any packet operations (WaitForDatapathReady, SendPacketAndCapture, etc.) because loadvm restores QEMU's internal stream-netdev state but the host-side UNIX socket connections are stale — Connect() short-circuits on the dead net.Conn and never reconnects.
Errors during reset are logged but do not cause the operation to fail.
func (*TestFramework) RestartGuestSerial ¶
func (f *TestFramework) RestartGuestSerial() error
RestartGuestSerial reconnects the serial console and restarts the reader goroutine after an AbortGuestSerial call.
func (*TestFramework) RestartYANET ¶
func (f *TestFramework) RestartYANET() error
func (*TestFramework) RestoreAndReconnect ¶
func (f *TestFramework) RestoreAndReconnect(snapshot string) error
RestoreAndReconnect reverts the VM to a previously saved snapshot and re-establishes the serial console and socket connections that break when the guest state is rolled back.
Call sequence after restore:
- Unmount 9P shares
- loadvm via QEMU monitor (monitor socket survives)
- Reconnect serial console
- Wait for shell prompts
- Mount 9P shares
- Reset socket connections (close stale connections, reconnect)
- Wait for dataplane ready (ICMP heartbeat)
Socket connections MUST be reset before the heartbeat because loadvm restores QEMU's internal stream-netdev state but the host-side UNIX socket connections are stale. Without reset, Connect() short-circuits on the dead net.Conn and heartbeat fails silently.
func (*TestFramework) RestoreBooted ¶
func (f *TestFramework) RestoreBooted() error
RestoreBooted restores the VM to the "booted" snapshot and re-establishes all connections. This is the primary per-test isolation primitive.
Unlike RestoreAndReconnect("baseline"), RestoreBooted does not depend on a baseline snapshot being created at test startup. It only requires that the VM pool was started from a pre-prepared booted template overlay.
func (*TestFramework) RestoreClean ¶
func (f *TestFramework) RestoreClean(snapshot string) error
RestoreClean reverts the VM to a previously saved snapshot and re-establishes serial console and 9P mounts WITHOUT running the dataplane heartbeat check. Use this for snapshots where YANET is not yet running (e.g. "preyanet") -- StartYANET will be called separately after restore.
func (*TestFramework) Run ¶
func (f *TestFramework) Run(name string, fn func(fw *TestFramework, t *testing.T)) bool
Run executes a subtest with the given name and function. This method wraps t.Run() and automatically creates a new TestFramework instance with the correct test name. This ensures that all framework operations within the subtest are properly tracked and logged.
The callback function receives two parameters:
- fw: A new *TestFramework instance with the correct test name set
- t: The *testing.T instance for the subtest
This design separates framework operations (via fw) from test assertions (via t), making the code more explicit and preventing accidental use of the wrong test context.
Parameters:
- name: The name of the subtest
- fn: The test function that receives fw and t
Returns:
- bool: True if the test passed, false otherwise (same as t.Run)
Example:
func TestMyFeature(t *testing.T) {
fw := globalFramework.ForTest(t)
fw.Run("BasicTest", func(fw *TestFramework, t *testing.T) {
input, output, err := fw.SendPacketAndParse(0, 0, packet, timeout)
require.NoError(t, err)
})
fw.Run("AdvancedTest", func(fw *TestFramework, t *testing.T) {
_, err := fw.ExecuteCommand("some command")
require.NoError(t, err)
})
}
func (*TestFramework) RunWith ¶
func (f *TestFramework) RunWith(snapshot string, name string, fn func(fw *TestFramework, t *testing.T)) bool
RunWith is like Run but restores the named VM snapshot before executing the test function. This gives each subtest a guaranteed clean VM state matching the configuration at snapshot time.
Use Run() (without snapshot restore) for subtests that intentionally build on the state left by previous subtests. Use RunWith() when each subtest needs full isolation.
func (*TestFramework) SSHPort ¶
func (f *TestFramework) SSHPort() int
SSHPort returns the loopback port forwarded to the guest SSH server.
func (*TestFramework) SaveSnapshot ¶
func (f *TestFramework) SaveSnapshot(name string) error
SaveSnapshot saves a named VM snapshot that captures the full machine state (CPU, RAM, devices). Subsequent calls to RestoreAndReconnect can revert the VM to this point. This is typically called from TestMain after YANET is configured to create a "baseline" snapshot.
The method unmounts all 9P shares before savevm (to remove QEMU migration blockers) and remounts them afterward. This only works cleanly when PrepareLocalStorage() was called first so that no process holds open fids on 9P mounts.
func (*TestFramework) SaveSnapshotKeepUnmounted ¶
func (f *TestFramework) SaveSnapshotKeepUnmounted(name string) error
SaveSnapshotKeepUnmounted saves a snapshot like SaveSnapshot but does NOT remount 9P shares afterward. Use this for baseline snapshots in pool mode: since RestoreAndReconnect will loadvm immediately, the intermediate remount would just be unmounted again before loadvm, wasting ~4s per test.
func (*TestFramework) SendPacketAndCapture ¶
func (f *TestFramework) SendPacketAndCapture(inputIfaceIndex int, outputIfaceIndex int, packet []byte, timeout time.Duration) ([]byte, error)
SendPacketAndCapture sends a network packet through the specified input interface and captures any response from the output interface without performing packet verification or parsing. This is a low-level method for raw packet testing.
The method handles:
- Socket client retrieval and connection management
- Packet transmission through the input interface
- Response capture from the output interface with timeout
- Automatic socket connection establishment
- Optional socket data dumping when debug mode is enabled
Parameters:
- inputIfaceIndex: Index of the network interface to send the packet through
- outputIfaceIndex: Index of the network interface to capture response from
- packet: Raw packet data to transmit
- timeout: Maximum time to wait for response capture
Returns:
- []byte: Raw response packet data, or nil if no response received
- error: An error if packet transmission fails or interfaces are unavailable
Example:
fw := globalFramework.ForTest(t)
fw.Run("SendPacket", func(fw *TestFramework, t *testing.T) {
response, err := fw.SendPacketAndCapture(0, 1, packetData, 5*time.Second)
require.NoError(t, err)
})
func (*TestFramework) SendPacketAndCaptureAll ¶
func (f *TestFramework) SendPacketAndCaptureAll(inputIfaceIndex int, outputIfaceIndex int, packet []byte, timeout time.Duration) ([][]byte, error)
SendPacketAndCaptureAll sends a network packet and captures all response packets.
func (*TestFramework) SendPacketAndCaptureAllUnfiltered ¶
func (f *TestFramework) SendPacketAndCaptureAllUnfiltered(inputIfaceIndex int, outputIfaceIndex int, packet []byte, timeout time.Duration) ([][]byte, error)
SendPacketAndCaptureAllUnfiltered captures every packet observed on the selected egress during timeout.
func (*TestFramework) SendPacketAndParse ¶
func (f *TestFramework) SendPacketAndParse(inputIfaceIndex int, outputIfaceIndex int, packet []byte, timeout time.Duration) (*PacketInfo, *PacketInfo, error)
SendPacketAndParse sends a network packet, captures the response, and parses both the input and output packets into structured PacketInfo objects. This high-level method provides comprehensive packet analysis for detailed testing scenarios.
The method performs:
- Input packet parsing and validation
- Packet transmission through the specified interfaces
- Response capture with timeout handling
- Output packet parsing and analysis
- Detailed logging of packet flow for debugging
- Optional socket data dumping when debug mode is enabled
Parameters:
- inputIfaceIndex: Index of the network interface to send the packet through
- outputIfaceIndex: Index of the network interface to capture response from
- packet: Raw packet data to transmit
- timeout: Maximum time to wait for response capture
Returns:
- *PacketInfo: Parsed information about the input packet
- *PacketInfo: Parsed information about the output packet (nil if no response)
- error: An error if packet processing, transmission, or parsing fails
Example:
fw := globalFramework.ForTest(t)
fw.Run("ParsePacket", func(fw *TestFramework, t *testing.T) {
input, output, err := fw.SendPacketAndParse(0, 1, packetData, 5*time.Second)
require.NoError(t, err)
t.Logf("Sent: %s, Received: %s", input.String(), output.String())
})
func (*TestFramework) SendPacketAndParseAll ¶
func (f *TestFramework) SendPacketAndParseAll(inputIfaceIndex int, outputIfaceIndex int, packet []byte, timeout time.Duration) ([]*PacketInfo, error)
SendPacketAndParseAll sends a network packet and captures ALL response packets.
func (*TestFramework) SendPacketsAndParseAll ¶
func (f *TestFramework) SendPacketsAndParseAll(inputIfaceIndex int, outputIfaceIndex int, packets [][]byte, timeout time.Duration) ([]*PacketInfo, error)
func (*TestFramework) Start ¶
func (f *TestFramework) Start() (bool, error)
Start initializes and launches the complete test environment, including the QEMU virtual machine with configured networking. This method must be called before executing any tests or VM operations.
The startup process includes:
- QEMU virtual machine launch with socket networking
- Network interface initialization
- VM readiness verification
Returns:
- error: An error if VM startup fails or networking cannot be established
Example:
if err := framework.Start(); err != nil {
log.Fatalf("Failed to start test environment: %v", err)
}
func (*TestFramework) StartLogTailers ¶
func (f *TestFramework) StartLogTailers() error
StartLogTailers starts background processes that tail YANET log files from local tmpfs to the 9P-mounted /mnt/logs/ directory, making logs visible on the host in real time. Must be called after YANET is running and 9P mounts are available.
func (*TestFramework) StartYANET ¶
func (f *TestFramework) StartYANET(dataplaneConfig string, controlplaneConfig string) error
StartYANET initializes and launches the complete YANET network processing stack within the virtual machine environment. This comprehensive method handles all aspects of YANET deployment including configuration, kernel module loading, network interface binding, and service startup.
The startup process includes:
- Configuration file creation and validation
- YANET binary availability verification
- Required kernel module loading (vfio-pci)
- Network interface binding to DPDK drivers
- YANET dataplane service startup with background execution
- YANET controlplane service startup and readiness verification
- Service health checks and log monitoring
Parameters:
- dataplaneConfig: YAML configuration content for the YANET dataplane service
- controlplaneConfig: YAML configuration content for the YANET controlplane service
Returns:
- error: An error if any step of the YANET startup process fails
Example:
dataplaneYAML := `
interfaces:
- name: "eth0"
pci: "01:00.0"
`
controlplaneYAML := `
modules:
- name: "forward"
`
if err := fw.StartYANET(dataplaneYAML, controlplaneYAML); err != nil {
log.Fatalf("YANET startup failed: %v", err)
}
func (*TestFramework) Stop ¶
func (f *TestFramework) Stop() error
Stop performs cleanup of the test environment, closing socket clients and stopping the QEMU virtual machine.
Multiple cleanup errors are collected and returned as a combined error.
func (*TestFramework) Unmount9P ¶
func (f *TestFramework) Unmount9P() error
Unmount9P unmounts all 9P shares inside the guest VM. This removes the QEMU migration blockers so that savevm/loadvm can proceed. When PrepareLocalStorage() has been called first, no process holds open fids on 9P mounts and plain umount works. Log tailer processes (tail -f >> /mnt/logs/...) are killed first because they hold open fids on /mnt/logs via write-append.
func (*TestFramework) WaitForDatapathReady ¶
func (f *TestFramework) WaitForDatapathReady(timeout time.Duration) error
WaitForDatapathReady sends ICMP heartbeat packets until the dataplane responds, confirming DPDK virtio-user reconnect after a snapshot restore. No operstate check — on macOS TCG and Linux KVM, kni0 operstate stays DOWN even when DPDK is actively forwarding. Only an end-to-end packet test works.
func (*TestFramework) WaitForReady ¶
func (f *TestFramework) WaitForReady(timeout time.Duration) error
WaitForReady blocks until the QEMU virtual machine becomes ready for command execution or the specified timeout expires. This is a proxy method for QEMU.WaitForReady.
Parameters:
- timeout: Maximum time to wait for VM readiness
Returns:
- error: An error if the timeout expires before VM becomes ready, or nil if ready
Example:
if err := fw.WaitForReady(60 * time.Second); err != nil {
log.Fatalf("VM failed to become ready: %v", err)
}
func (*TestFramework) WaitOutputPresent ¶
func (f *TestFramework) WaitOutputPresent(cmd string, checker func(string) bool, timeout time.Duration) error
WaitOutputPresent repeatedly executes a command until the output satisfies the provided checker function or the timeout expires. This utility method is used for waiting on asynchronous operations and service readiness verification.
The method polls the command output at regular intervals (100ms) and applies the checker function to determine if the expected condition has been met. This is particularly useful for waiting on service startup, configuration application, or system state changes.
Parameters:
- cmd: Shell command to execute repeatedly
- checker: Function that returns true when the desired condition is met
- timeout: Maximum time to wait for the condition
Returns:
- error: An error if the timeout expires or command execution fails
Example:
err := fw.WaitOutputPresent("ps aux | grep yanet", func(output string) bool {
return strings.Contains(output, "yanet-dataplane")
}, 30*time.Second)
func (*TestFramework) WriteGuestFile ¶
func (f *TestFramework) WriteGuestFile(path string, contents string) error
WriteGuestFile writes a file into the guest filesystem.
type UDPBuilder ¶
type UDPBuilder struct {
// contains filtered or unexported fields
}
func UDP ¶
func UDP(opts ...UDPOption) *UDPBuilder
func (*UDPBuilder) Build ¶
func (b *UDPBuilder) Build() gopacket.SerializableLayer
type UDPOption ¶
type UDPOption func(*UDPBuilder)
func UDPChecksumRaw ¶
UDPChecksumRaw sets an explicit checksum and prevents recomputation during serialization
func UDPLengthRaw ¶
UDPLengthRaw sets an explicit UDP length value from the original PCAP header. This is required for PCAP equivalence tests where we must preserve even malformed or unusual length values byte-for-byte.
type VMPool ¶
type VMPool struct {
// contains filtered or unexported fields
}
VMPool manages a pool of QEMU virtual machines for parallel test execution. Each VM slot holds a long-lived QEMUManager + framework instance. Pool slots are started from a pre-prepared template overlay (typically baseline, falling back to booted) and remain running between tests. Per-test isolation is achieved via RestoreBooted(), which does a fast loadvm+reconnect without restarting the QEMU process.
Pool size is controlled by YANET_VM_POOL_SIZE (default 1).
func NewVMPool ¶
func NewVMPool(size int, baseName string, qemuImage string, bootedTemplate string, templateOverlay string, templateSnapshotName string, enableSSHForward bool, log *zap.SugaredLogger) (_ *VMPool, err error)
NewVMPool creates a pool of size VMs. The bootedTemplate overlay is the canonical fallback source for pool slots. If templateOverlay/templateSnapshotName are provided and valid, StartAll prefers them. Otherwise it falls back to the booted template and bootstraps it if needed.
VMs are not started yet - call StartAll after creating the pool.
func (*VMPool) Acquire ¶
func (p *VMPool) Acquire() *TestFramework
Acquire blocks until a VM slot is available and returns its framework instance. The caller MUST call Release when done.
func (*VMPool) ForEachParallel ¶
func (p *VMPool) ForEachParallel(fn func(idx int, fw *TestFramework) error) error
ForEachParallel calls fn for each VM's framework instance in parallel.
func (*VMPool) ForceStop ¶
func (p *VMPool) ForceStop()
ForceStop makes every VM in the pool ignore the test keep-alive setting.
func (*VMPool) Release ¶
func (p *VMPool) Release(fw *TestFramework)
Release returns a VM slot back to the pool.
func (*VMPool) StartAll ¶
StartAll starts all VM slots. It prefers the configured template overlay when available and otherwise falls back to the cached booted template.
- Fast path: preferred template or booted template already exists -> copy it to each slot and boot via -loadvm <snapshot>.
- Slow path: booted template missing -> boot VM0 from scratch, save the booted snapshot, cache it as the template, then restart all slots from it.
After StartAll all slots are added to the available channel.
func (*VMPool) StopAllCPU ¶
func (p *VMPool) StopAllCPU()
StopAllCPU pauses every VM's CPU via the QEMU monitor. Useful after initial setup to avoid idle DPDK busy-polling consuming host CPU.