Skip to content
This repository was archived by the owner on Dec 27, 2023. It is now read-only.

Commit b278052

Browse files
committed
Split QemuSUT implementation
With this patch now we have a qemu_base.py module that can be imported to implement qemu based SUT. Signed-off-by: Andrea Cervesato <[email protected]>
1 parent 7111d31 commit b278052

File tree

3 files changed

+241
-201
lines changed

3 files changed

+241
-201
lines changed

ltp/qemu.py

Lines changed: 22 additions & 200 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""
22
.. module:: qemu
33
:platform: Linux
4-
:synopsis: module containing qemu SUT implementation
4+
:synopsis: module containing the base for qemu SUT implementation
55
66
.. moduleauthor:: Andrea Cervesato <[email protected]>
77
"""
@@ -11,7 +11,6 @@
1111
import signal
1212
import select
1313
import string
14-
import shutil
1514
import secrets
1615
import logging
1716
import threading
@@ -25,185 +24,50 @@
2524
from ltp.utils import LTPTimeoutError
2625

2726

28-
# pylint: disable=too-many-instance-attributes
29-
class QemuSUT(SUT):
27+
class QemuBase(SUT):
3028
"""
31-
Qemu SUT spawn a new VM using qemu and execute commands inside it.
32-
This SUT implementation can be used to run commands inside
33-
a protected, virtualized environment.
29+
This is a base class for qemu based SUT implementations.
3430
"""
3531

3632
def __init__(self) -> None:
3733
self._logger = logging.getLogger("ltp.qemu")
3834
self._comm_lock = threading.Lock()
3935
self._cmd_lock = threading.Lock()
4036
self._fetch_lock = threading.Lock()
41-
self._tmpdir = None
42-
self._env = None
43-
self._cwd = None
4437
self._proc = None
4538
self._poller = None
4639
self._stop = False
4740
self._logged_in = False
4841
self._last_pos = 0
49-
self._image = None
50-
self._image_overlay = None
51-
self._ro_image = None
52-
self._password = None
53-
self._ram = None
54-
self._smp = None
55-
self._virtfs = None
56-
self._serial_type = None
57-
self._qemu_cmd = None
58-
self._opts = None
5942
self._last_read = ""
6043

61-
@staticmethod
62-
def _generate_string(length: int = 10) -> str:
44+
def _get_command(self) -> str:
6345
"""
64-
Generate a random string of the given length.
46+
Return the full qemu command to execute.
6547
"""
66-
out = ''.join(secrets.choice(string.ascii_letters + string.digits)
67-
for _ in range(length))
68-
return out
48+
raise NotImplementedError()
49+
50+
def _login(self, timeout: float, iobuffer: IOBuffer) -> None:
51+
"""
52+
Method that implements login after starting the qemu process.
53+
"""
54+
raise NotImplementedError()
6955

70-
def _get_transport(self) -> str:
56+
def _get_transport(self) -> tuple:
7157
"""
7258
Return a couple of transport_dev and transport_file used by
7359
qemu instance for transport configuration.
7460
"""
75-
pid = os.getpid()
76-
transport_file = os.path.join(self._tmpdir, f"transport-{pid}")
77-
transport_dev = ""
78-
79-
if self._serial_type == "isa":
80-
transport_dev = "/dev/ttyS1"
81-
elif self._serial_type == "virtio":
82-
transport_dev = "/dev/vport1p1"
61+
raise NotImplementedError()
8362

84-
return transport_dev, transport_file
85-
86-
def _get_command(self) -> str:
63+
@staticmethod
64+
def _generate_string(length: int = 10) -> str:
8765
"""
88-
Return the full qemu command to execute.
66+
Generate a random string of the given length.
8967
"""
90-
pid = os.getpid()
91-
tty_log = os.path.join(self._tmpdir, f"ttyS0-{pid}.log")
92-
93-
image = self._image
94-
if self._image_overlay:
95-
shutil.copyfile(
96-
self._image,
97-
self._image_overlay)
98-
image = self._image_overlay
99-
100-
params = []
101-
params.append("-enable-kvm")
102-
params.append("-display none")
103-
params.append(f"-m {self._ram}")
104-
params.append(f"-smp {self._smp}")
105-
params.append("-device virtio-rng-pci")
106-
params.append(f"-drive if=virtio,cache=unsafe,file={image}")
107-
params.append(f"-chardev stdio,id=tty,logfile={tty_log}")
108-
109-
if self._serial_type == "isa":
110-
params.append("-serial chardev:tty")
111-
params.append("-serial chardev:transport")
112-
elif self._serial_type == "virtio":
113-
params.append("-device virtio-serial")
114-
params.append("-device virtconsole,chardev=tty")
115-
params.append("-device virtserialport,chardev=transport")
116-
else:
117-
raise SUTError(
118-
f"Unsupported serial device type {self._serial_type}")
119-
120-
_, transport_file = self._get_transport()
121-
params.append(f"-chardev file,id=transport,path={transport_file}")
122-
123-
if self._ro_image:
124-
params.append(
125-
"-drive read-only,"
126-
"if=virtio,"
127-
"cache=unsafe,"
128-
f"file={self._ro_image}")
129-
130-
if self._virtfs:
131-
params.append(
132-
"-virtfs local,"
133-
f"path={self._virtfs},"
134-
"mount_tag=host0,"
135-
"security_model=mapped-xattr,"
136-
"readonly=on")
137-
138-
if self._opts:
139-
params.append(self._opts)
140-
141-
cmd = f"{self._qemu_cmd} {' '.join(params)}"
142-
143-
return cmd
144-
145-
def setup(self, **kwargs: dict) -> None:
146-
self._logger.info("Initialize SUT")
147-
148-
self._env = kwargs.get("env", None)
149-
self._cwd = kwargs.get("cwd", None)
150-
self._tmpdir = kwargs.get("tmpdir", None)
151-
self._image = kwargs.get("image", None)
152-
self._image_overlay = kwargs.get("image_overlay", None)
153-
self._ro_image = kwargs.get("ro_image", None)
154-
self._password = kwargs.get("password", "root")
155-
self._ram = kwargs.get("ram", "2G")
156-
self._smp = kwargs.get("smp", "2")
157-
self._virtfs = kwargs.get("virtfs", None)
158-
self._serial_type = kwargs.get("serial", "isa")
159-
self._opts = kwargs.get("options", None)
160-
161-
system = kwargs.get("system", "x86_64")
162-
self._qemu_cmd = f"qemu-system-{system}"
163-
164-
if not self._tmpdir or not os.path.isdir(self._tmpdir):
165-
raise SUTError(
166-
f"Temporary directory doesn't exist: {self._tmpdir}")
167-
168-
if not self._image or not os.path.isfile(self._image):
169-
raise SUTError(
170-
f"Image location doesn't exist: {self._image}")
171-
172-
if self._ro_image and not os.path.isfile(self._ro_image):
173-
raise SUTError(
174-
f"Read-only image location doesn't exist: {self._ro_image}")
175-
176-
if not self._ram:
177-
raise SUTError("RAM is not defined")
178-
179-
if not self._smp:
180-
raise SUTError("CPU is not defined")
181-
182-
if self._virtfs and not os.path.isdir(self._virtfs):
183-
raise SUTError(
184-
f"Virtual FS directory doesn't exist: {self._virtfs}")
185-
186-
if self._serial_type not in ["isa", "virtio"]:
187-
raise SUTError("Serial protocol must be isa or virtio")
188-
189-
@property
190-
def config_help(self) -> dict:
191-
return {
192-
"image": "qcow2 image location",
193-
"image_overlay": "image_overlay: image copy location",
194-
"password": "root password (default: root)",
195-
"system": "system architecture (default: x86_64)",
196-
"ram": "RAM of the VM (default: 2G)",
197-
"smp": "number of CPUs (default: 2)",
198-
"serial": "type of serial protocol. isa|virtio (default: isa)",
199-
"virtfs": "directory to mount inside VM",
200-
"ro_image": "path of the image that will exposed as read only",
201-
"options": "user defined options",
202-
}
203-
204-
@property
205-
def name(self) -> str:
206-
return "qemu"
68+
out = ''.join(secrets.choice(string.ascii_letters + string.digits)
69+
for _ in range(length))
70+
return out
20771

20872
@property
20973
def is_running(self) -> bool:
@@ -470,9 +334,6 @@ def communicate(
470334
self,
471335
timeout: float = 3600,
472336
iobuffer: IOBuffer = None) -> None:
473-
if not shutil.which(self._qemu_cmd):
474-
raise SUTError(f"Command not found: {self._qemu_cmd}")
475-
476337
if self.is_running:
477338
raise SUTError("Virtual machine is already running")
478339

@@ -503,48 +364,9 @@ def communicate(
503364
select.POLLERR)
504365

505366
try:
506-
self._wait_for("login:", timeout, iobuffer)
507-
self._write_stdin("root\n")
508-
509-
if self._password:
510-
self._wait_for("Password:", 5, iobuffer)
511-
self._write_stdin(f"{self._password}\n")
512-
513-
time.sleep(0.2)
514-
515-
self._wait_for("#", 5, iobuffer)
516-
time.sleep(0.2)
517-
518-
self._write_stdin("stty -echo; stty cols 1024\n")
519-
self._wait_for("#", 5, None)
520-
521-
_, retcode, _ = self._exec("export PS1=''", 5, None)
522-
if retcode != 0:
523-
raise SUTError("Can't setup prompt string")
524-
525-
if self._virtfs:
526-
_, retcode, _ = self._exec(
527-
"mount -t 9p -o trans=virtio host0 /mnt",
528-
10, None)
529-
if retcode != 0:
530-
raise SUTError("Failed to mount virtfs")
531-
532-
if self._cwd:
533-
_, retcode, _ = self._exec(f"cd {self._cwd}", 5, None)
534-
if retcode != 0:
535-
raise SUTError("Can't setup current working directory")
536-
537-
if self._env:
538-
for key, value in self._env.items():
539-
_, retcode, _ = self._exec(
540-
f"export {key}={value}",
541-
5, None)
542-
if retcode != 0:
543-
raise SUTError(f"Can't setup env {key}={value}")
544-
367+
self._login(timeout, iobuffer)
545368
self._logged_in = True
546-
547-
self._logger.info("Virtual machine started")
369+
self._logger.info("Logged inside virtual machine")
548370
except SUTError as err:
549371
error = err
550372

0 commit comments

Comments
 (0)