Назад | Перейти на главную страницу

Как без конфликтов перенаправить порты в QEMU пользовательского режима?

Я запускаю Linux под QEMU как часть набора тестов для скрипта Python, который выполняет некоторые привилегированные операции. Для меня важно запустить полноценную виртуальную машину, потому что:

  1. Я не хочу требовать доступа sudo для запуска тестов
  2. позже будет проще запускать тесты в разных дистрибутивах и версиях ядра
  3. У меня меньше шансов сломать свой компьютер

Мой подход состоит в том, чтобы запустить виртуальную машину QEMU с портом хоста, перенаправленным на 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
# ...

Гостевая ОС подходит.

Вещи, которые я пробовал:

После небольшого дополнительного исследования: 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) и избегая конфликтов портов на хосте:

  1. Для каждого приложения создайте сопоставление портов через командную строку (-netdev) или монитор (netdev_add), обеспечивая 0 для хост-порта
  2. Разоблачить monitor как сокет unix для приложения-потребителя
  3. Выполнить info usernet через сокет и извлеките порт источника для каждого сопоставления, который является фактическим портом, выделенным ОС.