У меня есть программа на Python2, которая запускает qemu с образом FreeBSD.
expect()
строковый вывод работает.
Однако вывод expect()
, у которого не завершена строка (например, при ожидании приглашения типа login:
), на этот раз не срабатывает.
Я подозреваю, что что-то в связи между qemu и моей программой выполняет буферизацию строки, но как мне узнать, что это такое? Кандидаты, о которых я могу думать:
- Сам FreeBSD. Я считаю, что маловероятно, что в интерактивном режиме показываются подсказки, а параметры
-nographics
в qemu не должны иметь значения для эмулируемой виртуальной машины (но я могу ошибаться).
- Что-то в настройке pty. У меня нулевой опыт работы с ptys. Если это проблема, то это будет ошибка в pexpect, так как pexpect устанавливает pty.
- Ошибка в pexpect.
- Что-то в моем собственном сценарии ... но я понятия не имею, что это может быть.
Для справки вот урезанный код (включая скачивание и распаковку, если кто-то захочет поиграть с ним):
#! /usr/bin/env python2
import os
import pexpect
import re
import sys
import time
def run(cmd):
'''Run command, log to stdout, no timeout, return the status code.'''
print('run: ' + cmd)
(output, rc) = pexpect.run(
cmd,
withexitstatus=1,
encoding='utf-8',
logfile=sys.stdout,
timeout=None
)
if rc != 0:
print('simple.py: Command failed with return code: ' + rc)
exit(rc)
download_path = 'https://download.freebsd.org/ftp/releases/VM-IMAGES/12.0-RELEASE/amd64/Latest'
image_file = 'FreeBSD-12.0-RELEASE-amd64.qcow2'
image_file_xz = image_file + '.xz'
if not os.path.isfile(image_file_xz):
run('curl -o %s %s/%s' % (image_file_xz, download_path, image_file_xz))
if not os.path.isfile(image_file):
# Reset image file to initial state
run('xz --decompress --keep --force --verbose ' + image_file_xz)
#cmd = 'qemu-system-x86_64 -snapshot -monitor none -display curses -chardev stdio,id=char0 ' + image_file
cmd = 'qemu-system-x86_64 -snapshot -nographic ' + image_file
print('interact with: ' + cmd)
child = pexpect.spawn(
cmd,
timeout=90, # FreeBSD takes roughly 60 seconds to boot
maxread=1,
)
child.logfile = sys.stdout
def expect(pattern):
result = child.expect([pexpect.TIMEOUT, pattern])
if result == 0:
print("timeout: %d reached when waiting for: %s" % (child.timeout, pattern))
exit(1)
return result - 1
if False:
# This does not work: the prompt is not visible, then timeout
expect('login: ')
else:
# Workaround, tested to work:
expect(re.escape('FreeBSD/amd64 (freebsd)')) # Line before prompt
time.sleep(1) # MUCH longer than actually needed, just to be safe
child.sendline('root')
# This will always time out, and terminate the script
expect('# ')
print('We want to get here but cannot')