我在 QEMU 下运行 Linux,作为执行某些特权操作的 Python 脚本的测试套件的一部分。运行完整的虚拟机对我来说很重要,因为:
- 我不想需要 sudo 访问权限来运行测试
- 以后将更容易在不同的发行版和内核版本上运行测试
- 我不太可能弄坏我的电脑
我的方法是启动 QEMU VM,并将主机端口转发到 VM 上的 SSH,通过 SSH 发送并运行脚本,运行脚本,然后断言有关远程系统的信息。此设置的麻烦之处在于 SSH 端口和其他网络应用程序的端口。
我的问题:如何在用户模式 QEMU 中转发端口而不发生冲突?
我的目标是能够同时运行多个测试,无需任何事先设置,也无需 sudo,如上所述。QEMU 中的用户模式网络支持端口转发,当我0
在 hostfwd 声明 () 中传递主机端口时hostfwd=tcp:127.0.0.1:0-:22
,操作系统会动态分配一个端口。太棒了!问题在于如何在父进程中可靠地获取该端口。
我正在做的事情相当于:
#!/bin/sh
set -e
cd "$(mktemp -d)"
# Download cloud image.
image_base_url="https://cloud-images.ubuntu.com/releases/18.04/release/"
image_name="ubuntu-18.04-server-cloudimg-amd64.img"
image_url="$image_base_url$image_name"
curl --location -o linux.img $image_url
# Create SSH key.
ssh_key_path="$PWD/id_rsa"
ssh-keygen -t rsa -b 4096 -N "" -f "$ssh_key_path"
# Create cloud-init payload.
cat <<EOF > user_data
#cloud-config
password: ubuntu
chpasswd: { expire: False }
ssh_pwauth: True
ssh_authorized_keys:
- $(cat "${ssh_key_path}.pub")
EOF
cloud-localds user_data.img user_data
# Socket path for QMP.
qmp_socket="$PWD/qmp.sock"
# Start VM.
qemu-system-x86_64 \
-drive file=linux.img,format=qcow2 \
-drive file=user_data.img,format=raw \
-netdev user,id=net0,hostfwd=tcp:127.0.0.1:0-:22 \
-device rtl8139,netdev=net0 \
-enable-kvm \
-m 2G \
-serial mon:stdio \
-nographic \
-smp 2 \
-snapshot \
-qmp unix:$qmp_socket,server,nowait \
& \
;
# wait a bit, then test stuff here
# ...
客户操作系统运行正常。
我尝试过的事情:
玩了一下定量分子泵,但找不到提供信息的命令
考虑在父进程中绑定一个随机端口,
SO_REUSEPORT
然后为子命令指定它,但 QEMU 不使用此标志,因此它不起作用netstat -nalp | grep $child_pid
,但在转发多个端口时将无法使用选择一个随机未绑定的端口,尝试用它启动 qemu,如果失败并出现匹配的消息,则重试
Could not set up host forwarding rule 'tcp:...'
。这有效,但是- 端口越多越容易发生故障
- 可能会掩盖其他不值得重试的问题
这是一个不错的后备方案,但我希望有一个更直接的解决方案。
答案1
经过进一步研究:QEMU 通过info usernet
命令公开信息。输出如下所示:
(qemu) info usernet
VLAN -1 (net0):
Protocol[State] FD Source Address Port Dest. Address Port RecvQ SendQ
TCP[HOST_FORWARD] 13 127.0.0.1 38117 10.0.2.15 22 0 0
UDP[236 sec] 24 10.0.2.15 35061 91.189.89.198 123 0 0
UDP[204 sec] 26 10.0.2.15 60630 91.189.89.198 123 0 0
它无法通过 QMP 获得,但已提交补丁以添加等效的query-usernet
这里前一段时间。
就我而言,使用 公开监视器-monitor unix:$PWD/mon.sock,server,nowait
、连接、发送命令、读取输出并解析所需的值非常简单。
总而言之,使用 QEMU 测试网络应用程序而不需要 sudo 或其他先决条件(QEMU 本身除外),并避免主机上的端口冲突:
- 对于每个应用程序,通过命令行(
-netdev
)或监视器(netdev_add
)创建端口映射,0
为主机端口提供 monitor
将作为 unix 套接字公开给使用应用程序- 通过套接字执行
info usernet
,并提取每个映射的源端口,这是操作系统分配的实际端口