Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Unix domain socket support for VLAN #17473

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions pkg/machine/e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,22 @@ Note: To run specific test files, add the test files to the end of the winmake c
1. `export CONTAINERS_MACHINE_PROVIDER="applehv"`
1. `export MACHINE_IMAGE="https://fedorapeople.org/groups/podman/testing/applehv/arm64/fedora-coreos-38.20230925.dev.0-applehv.aarch64.raw.gz"`
1. `make localmachine` (Add `FOCUS_FILE=basic_test.go` to only run basic test)

### QEMU (fd vlan)

1. Install Podman and QEMU for MacOS bundle using latest release from https://github.com/containers/podman/releases
1. `make podman-remote`
1. `export CONTAINERS_MACHINE_PROVIDER="qemu"`
1. Add bundled QEMU to path `export PATH=/opt/podman/qemu/bin:$PATH`
1. Set search path to gvproxy from bundle `export CONTAINERS_HELPER_BINARY_DIR=/opt/podman/bin`
1. `make localmachine` (Add `FOCUS_FILE=basic_test.go` to only run basic test)

### QEMU (UNIX domain socket vlan)

1. Install Podman and QEMU for MacOS bundle using latest release from https://github.com/containers/podman/releases
1. `make podman-remote`
1. `export CONTAINERS_MACHINE_PROVIDER="qemu"`
1. Add bundled QEMU to path `export PATH=/opt/podman/qemu/bin:$PATH`
1. Set search path to gvproxy from bundle `export CONTAINERS_HELPER_BINARY_DIR=/opt/podman/bin`
1. `export CONTAINERS_USE_SOCKET_VLAN=true`
1. `make localmachine` (Add `FOCUS_FILE=basic_test.go` to only run basic test)
4 changes: 2 additions & 2 deletions pkg/machine/machine_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ func StopWinProxy(name string, vmtype define.VMType) error {
if err != nil {
return nil
}
sendQuit(tid)
SendQuit(tid)
_ = waitTimeout(proc, 20*time.Second)
_ = os.Remove(tidFile)

Expand Down Expand Up @@ -200,7 +200,7 @@ func waitTimeout(proc *os.Process, timeout time.Duration) bool {
return ret
}

func sendQuit(tid uint32) {
func SendQuit(tid uint32) {
user32 := syscall.NewLazyDLL("user32.dll")
postMessage := user32.NewProc("PostThreadMessageW")
postMessage.Call(uintptr(tid), WM_QUIT, 0, 0)
Expand Down
24 changes: 21 additions & 3 deletions pkg/machine/qemu/command/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package command

import (
"encoding/base64"
"errors"
"fmt"
"os"
"path/filepath"
Expand All @@ -14,6 +15,11 @@ import (
"github.com/containers/podman/v4/pkg/machine/define"
)

const (
FdVlanNetdev = "socket,id=vlan,fd=3"
vlanMac = "5a:94:ef:e4:0c:ee"
)

// QemuCmd is an alias around a string slice to prevent the need to migrate the
// MachineVM struct due to changes
type QemuCmd []string
Expand Down Expand Up @@ -47,13 +53,25 @@ func (q *QemuCmd) SetQmpMonitor(monitor Monitor) {
}

// SetNetwork adds a network device to the machine
func (q *QemuCmd) SetNetwork() {
func (q *QemuCmd) SetNetwork(vlanSocket *define.VMFile) error {
// Right now the mac address is hardcoded so that the host networking gives it a specific IP address. This is
// why we can only run one vm at a time right now
*q = append(*q, "-netdev", "socket,id=vlan,fd=3", "-device", "virtio-net-pci,netdev=vlan,mac=5a:94:ef:e4:0c:ee")
if UseFdVLan() {
*q = append(*q, []string{"-netdev", FdVlanNetdev}...)
} else {
if vlanSocket == nil {
return errors.New("vlanSocket is undefined")
}
*q = append(*q, []string{"-netdev", socketVlanNetdev(vlanSocket.GetPath())}...)
}
*q = append(*q, []string{"-device", "virtio-net-pci,netdev=vlan,mac=" + vlanMac}...)
return nil
}

func socketVlanNetdev(path string) string {
return fmt.Sprintf("stream,id=vlan,server=off,addr.type=unix,addr.path=%s", path)
}

// SetNetwork adds a network device to the machine
func (q *QemuCmd) SetUSBHostPassthrough(usbs []USBConfig) {
if len(usbs) == 0 {
return
Expand Down
13 changes: 13 additions & 0 deletions pkg/machine/qemu/command/command_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd
// +build darwin dragonfly freebsd linux netbsd openbsd

package command

import (
"os"
"strings"
)

func UseFdVLan() bool {
return strings.ToUpper(os.Getenv("CONTAINERS_USE_SOCKET_VLAN")) != "TRUE"
}
5 changes: 5 additions & 0 deletions pkg/machine/qemu/command/command_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package command

func UseFdVLan() bool {
return false
}
69 changes: 68 additions & 1 deletion pkg/machine/qemu/command/qemu_command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ func TestQemuCmd(t *testing.T) {
cmd.SetCPUs(4)
cmd.SetIgnitionFile(*ignFile)
cmd.SetQmpMonitor(monitor)
cmd.SetNetwork()
err = cmd.SetNetwork(nil)
assert.NoError(t, err)
cmd.SetSerialPort(*readySocket, *vmPidFile, "test-machine")
cmd.SetVirtfsMount("/tmp/path", "vol10", "none", true)
cmd.SetBootableImage(bootableImagePath)
Expand All @@ -64,3 +65,69 @@ func TestQemuCmd(t *testing.T) {

require.Equal(t, cmd.Build(), expected)
}

func TestQemuCmdUnixVlanMissingSocket(t *testing.T) {
t.Setenv("CONTAINERS_USE_SOCKET_VLAN", "true")
cmd := NewQemuBuilder("/usr/bin/qemu-system-x86_64", []string{})
err := cmd.SetNetwork(nil)
assert.Error(t, err)
}

func TestQemuCmdUnixVlan(t *testing.T) {
t.Setenv("CONTAINERS_USE_SOCKET_VLAN", "true")
ignFile, err := define.NewMachineFile(t.TempDir()+"demo-ignition-file.ign", nil)
assert.NoError(t, err)

machineAddrFile, err := define.NewMachineFile(t.TempDir()+"tmp.sock", nil)
assert.NoError(t, err)

vlanSocket, err := define.NewMachineFile(t.TempDir()+"vlan.sock", nil)
assert.NoError(t, err)

readySocket, err := define.NewMachineFile(t.TempDir()+"readySocket.sock", nil)
assert.NoError(t, err)

vmPidFile, err := define.NewMachineFile(t.TempDir()+"vmpidfile.pid", nil)
assert.NoError(t, err)

monitor := Monitor{
Address: *machineAddrFile,
Network: "unix",
Timeout: 3,
}
ignPath := ignFile.GetPath()
addrFilePath := machineAddrFile.GetPath()
readySocketPath := readySocket.GetPath()
vmPidFilePath := vmPidFile.GetPath()
bootableImagePath := t.TempDir() + "test-machine_fedora-coreos-38.20230918.2.0-qemu.x86_64.qcow2"

cmd := NewQemuBuilder("/usr/bin/qemu-system-x86_64", []string{})
cmd.SetMemory(2048)
cmd.SetCPUs(4)
cmd.SetIgnitionFile(*ignFile)
cmd.SetQmpMonitor(monitor)
err = cmd.SetNetwork(vlanSocket)
assert.NoError(t, err)
cmd.SetSerialPort(*readySocket, *vmPidFile, "test-machine")
cmd.SetVirtfsMount("/tmp/path", "vol10", "none", true)
cmd.SetBootableImage(bootableImagePath)
cmd.SetDisplay("none")

expected := []string{
"/usr/bin/qemu-system-x86_64",
"-m", "2048",
"-smp", "4",
"-fw_cfg", fmt.Sprintf("name=opt/com.coreos/config,file=%s", ignPath),
"-qmp", fmt.Sprintf("unix:%s,server=on,wait=off", addrFilePath),
"-netdev", socketVlanNetdev(vlanSocket.GetPath()),
"-device", "virtio-net-pci,netdev=vlan,mac=5a:94:ef:e4:0c:ee",
"-device", "virtio-serial",
"-chardev", fmt.Sprintf("socket,path=%s,server=on,wait=off,id=atest-machine_ready", readySocketPath),
"-device", "virtserialport,chardev=atest-machine_ready,name=org.fedoraproject.port.0",
"-pidfile", vmPidFilePath,
"-virtfs", "local,path=/tmp/path,mount_tag=vol10,security_model=none,readonly",
"-drive", fmt.Sprintf("if=virtio,file=%s", bootableImagePath),
"-display", "none"}

require.Equal(t, cmd.Build(), expected)
}
15 changes: 12 additions & 3 deletions pkg/machine/qemu/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,22 @@ func (v *MachineVM) setQMPMonitorSocket() error {

// setNewMachineCMD configure the CLI command that will be run to create the new
// machine
func (v *MachineVM) setNewMachineCMD(qemuBinary string, cmdOpts *setNewMachineCMDOpts) {
func (v *MachineVM) setNewMachineCMD(qemuBinary string, cmdOpts *setNewMachineCMDOpts) error {
v.CmdLine = command.NewQemuBuilder(qemuBinary, v.addArchOptions(cmdOpts))
v.CmdLine.SetMemory(v.Memory)
v.CmdLine.SetCPUs(v.CPUs)
v.CmdLine.SetIgnitionFile(v.IgnitionFile)
v.CmdLine.SetQmpMonitor(v.QMPMonitor)
v.CmdLine.SetNetwork()
vlanSocket, err := machineSocket(v.Name, "vlan", "")
if err != nil {
return err
}
if err := v.CmdLine.SetNetwork(vlanSocket); err != nil {
return err
}
v.CmdLine.SetSerialPort(v.ReadySocket, v.VMPidFilePath, v.Name)
v.CmdLine.SetUSBHostPassthrough(v.USBs)
return nil
}

// NewMachine initializes an instance of a virtual machine based on the qemu
Expand Down Expand Up @@ -146,7 +153,9 @@ func (p *QEMUVirtualization) NewMachine(opts machine.InitOptions) (machine.VM, e

// configure command to run
cmdOpts := setNewMachineCMDOpts{imageDir: dataDir}
vm.setNewMachineCMD(execPath, &cmdOpts)
if err := vm.setNewMachineCMD(execPath, &cmdOpts); err != nil {
return nil, err
}
return vm, nil
}

Expand Down
Loading