iotests/081: Filter image format after testdir
[qemu.git] / tests / acceptance / virtiofs_submounts.py
blob361e5990b65688eb0d165aceb3a0616f154a55bc
1 import logging
2 import re
3 import os
4 import subprocess
5 import time
7 from avocado import skipUnless
8 from avocado_qemu import Test, BUILD_DIR
9 from avocado_qemu import wait_for_console_pattern
10 from avocado.utils import ssh
12 from qemu.accel import kvm_available
14 from boot_linux import BootLinux
17 def run_cmd(args):
18 subp = subprocess.Popen(args,
19 stdout=subprocess.PIPE,
20 stderr=subprocess.PIPE,
21 universal_newlines=True)
22 stdout, stderr = subp.communicate()
23 ret = subp.returncode
25 return (stdout, stderr, ret)
27 def has_cmd(name, args=None):
28 """
29 This function is for use in a @avocado.skipUnless decorator, e.g.:
31 @skipUnless(*has_cmd('sudo -n', ('sudo', '-n', 'true')))
32 def test_something_that_needs_sudo(self):
33 ...
34 """
36 if args is None:
37 args = ('which', name)
39 try:
40 _, stderr, exitcode = run_cmd(args)
41 except Exception as e:
42 exitcode = -1
43 stderr = str(e)
45 if exitcode != 0:
46 cmd_line = ' '.join(args)
47 err = f'{name} required, but "{cmd_line}" failed: {stderr.strip()}'
48 return (False, err)
49 else:
50 return (True, '')
52 def has_cmds(*cmds):
53 """
54 This function is for use in a @avocado.skipUnless decorator and
55 allows checking for the availability of multiple commands, e.g.:
57 @skipUnless(*has_cmds(('cmd1', ('cmd1', '--some-parameter')),
58 'cmd2', 'cmd3'))
59 def test_something_that_needs_cmd1_and_cmd2(self):
60 ...
61 """
63 for cmd in cmds:
64 if isinstance(cmd, str):
65 cmd = (cmd,)
67 ok, errstr = has_cmd(*cmd)
68 if not ok:
69 return (False, errstr)
71 return (True, '')
74 class VirtiofsSubmountsTest(BootLinux):
75 """
76 :avocado: tags=arch:x86_64
77 """
79 def get_portfwd(self):
80 port = None
82 res = self.vm.command('human-monitor-command',
83 command_line='info usernet')
84 for line in res.split('\r\n'):
85 match = \
86 re.search(r'TCP.HOST_FORWARD.*127\.0\.0\.1\s*(\d+)\s+10\.',
87 line)
88 if match is not None:
89 port = match[1]
90 break
92 self.assertIsNotNone(port)
93 self.log.debug('sshd listening on port: ' + port)
94 return port
96 def ssh_connect(self, username, keyfile):
97 self.ssh_logger = logging.getLogger('ssh')
98 port = self.get_portfwd()
99 self.ssh_session = ssh.Session('127.0.0.1', port=int(port),
100 user=username, key=keyfile)
101 for i in range(10):
102 try:
103 self.ssh_session.connect()
104 return
105 except:
106 time.sleep(4)
107 pass
108 self.fail('sshd timeout')
110 def ssh_command(self, command):
111 self.ssh_logger.info(command)
112 result = self.ssh_session.cmd(command)
113 stdout_lines = [line.rstrip() for line
114 in result.stdout_text.splitlines()]
115 for line in stdout_lines:
116 self.ssh_logger.info(line)
117 stderr_lines = [line.rstrip() for line
118 in result.stderr_text.splitlines()]
119 for line in stderr_lines:
120 self.ssh_logger.warning(line)
122 self.assertEqual(result.exit_status, 0,
123 f'Guest command failed: {command}')
124 return stdout_lines, stderr_lines
126 def run(self, args, ignore_error=False):
127 stdout, stderr, ret = run_cmd(args)
129 if ret != 0:
130 cmdline = ' '.join(args)
131 if not ignore_error:
132 self.fail(f'{cmdline}: Returned {ret}: {stderr}')
133 else:
134 self.log.warn(f'{cmdline}: Returned {ret}: {stderr}')
136 return (stdout, stderr, ret)
138 def set_up_shared_dir(self):
139 atwd = os.getenv('AVOCADO_TEST_WORKDIR')
140 self.shared_dir = os.path.join(atwd, 'virtiofs-shared')
142 os.mkdir(self.shared_dir)
144 self.run(('cp', self.get_data('guest.sh'),
145 os.path.join(self.shared_dir, 'check.sh')))
147 self.run(('cp', self.get_data('guest-cleanup.sh'),
148 os.path.join(self.shared_dir, 'cleanup.sh')))
150 def set_up_virtiofs(self):
151 attmp = os.getenv('AVOCADO_TESTS_COMMON_TMPDIR')
152 self.vfsdsock = os.path.join(attmp, 'vfsdsock')
154 self.run(('sudo', '-n', 'rm', '-f', self.vfsdsock), ignore_error=True)
156 self.virtiofsd = \
157 subprocess.Popen(('sudo', '-n',
158 'tools/virtiofsd/virtiofsd',
159 f'--socket-path={self.vfsdsock}',
160 '-o', f'source={self.shared_dir}',
161 '-o', 'cache=always',
162 '-o', 'xattr',
163 '-o', 'announce_submounts',
164 '-f'),
165 stdout=subprocess.DEVNULL,
166 stderr=subprocess.PIPE,
167 universal_newlines=True)
169 while not os.path.exists(self.vfsdsock):
170 if self.virtiofsd.poll() is not None:
171 self.fail('virtiofsd exited prematurely: ' +
172 self.virtiofsd.communicate()[1])
173 time.sleep(0.1)
175 self.run(('sudo', '-n', 'chmod', 'go+rw', self.vfsdsock))
177 self.vm.add_args('-chardev',
178 f'socket,id=vfsdsock,path={self.vfsdsock}',
179 '-device',
180 'vhost-user-fs-pci,queue-size=1024,chardev=vfsdsock' \
181 ',tag=host',
182 '-object',
183 'memory-backend-file,id=mem,size=1G,' \
184 'mem-path=/dev/shm,share=on',
185 '-numa',
186 'node,memdev=mem')
188 def launch_vm(self):
189 self.launch_and_wait()
190 self.ssh_connect('root', self.ssh_key)
192 def set_up_nested_mounts(self):
193 scratch_dir = os.path.join(self.shared_dir, 'scratch')
194 try:
195 os.mkdir(scratch_dir)
196 except FileExistsError:
197 pass
199 args = ['bash', self.get_data('host.sh'), scratch_dir]
200 if self.seed:
201 args += [self.seed]
203 out, _, _ = self.run(args)
204 seed = re.search(r'^Seed: \d+', out)
205 self.log.info(seed[0])
207 def mount_in_guest(self):
208 self.ssh_command('mkdir -p /mnt/host')
209 self.ssh_command('mount -t virtiofs host /mnt/host')
211 def check_in_guest(self):
212 self.ssh_command('bash /mnt/host/check.sh /mnt/host/scratch/share')
214 def live_cleanup(self):
215 self.ssh_command('bash /mnt/host/cleanup.sh /mnt/host/scratch')
217 # It would be nice if the above was sufficient to make virtiofsd clear
218 # all references to the mounted directories (so they can be unmounted
219 # on the host), but unfortunately it is not. To do so, we have to
220 # resort to a remount.
221 self.ssh_command('mount -o remount /mnt/host')
223 scratch_dir = os.path.join(self.shared_dir, 'scratch')
224 self.run(('bash', self.get_data('cleanup.sh'), scratch_dir))
226 @skipUnless(*has_cmds(('sudo -n', ('sudo', '-n', 'true')),
227 'ssh-keygen', 'bash', 'losetup', 'mkfs.xfs', 'mount'))
228 def setUp(self):
229 vmlinuz = self.params.get('vmlinuz')
230 if vmlinuz is None:
231 self.cancel('vmlinuz parameter not set; you must point it to a '
232 'Linux kernel binary to test (to run this test with ' \
233 'the on-image kernel, set it to an empty string)')
235 self.seed = self.params.get('seed')
237 atwd = os.getenv('AVOCADO_TEST_WORKDIR')
238 self.ssh_key = os.path.join(atwd, 'id_ed25519')
240 self.run(('ssh-keygen', '-t', 'ed25519', '-f', self.ssh_key))
242 pubkey = open(self.ssh_key + '.pub').read()
244 super(VirtiofsSubmountsTest, self).setUp(pubkey)
246 if len(vmlinuz) > 0:
247 self.vm.add_args('-kernel', vmlinuz,
248 '-append', 'console=ttyS0 root=/dev/sda1')
250 # Allow us to connect to SSH
251 self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22',
252 '-device', 'e1000,netdev=vnet')
254 if not kvm_available(self.arch, self.qemu_bin):
255 self.cancel(KVM_NOT_AVAILABLE)
256 self.vm.add_args('-accel', 'kvm')
258 def tearDown(self):
259 try:
260 self.vm.shutdown()
261 except:
262 pass
264 scratch_dir = os.path.join(self.shared_dir, 'scratch')
265 self.run(('bash', self.get_data('cleanup.sh'), scratch_dir),
266 ignore_error=True)
268 def test_pre_virtiofsd_set_up(self):
269 self.set_up_shared_dir()
271 self.set_up_nested_mounts()
273 self.set_up_virtiofs()
274 self.launch_vm()
275 self.mount_in_guest()
276 self.check_in_guest()
278 def test_pre_launch_set_up(self):
279 self.set_up_shared_dir()
280 self.set_up_virtiofs()
282 self.set_up_nested_mounts()
284 self.launch_vm()
285 self.mount_in_guest()
286 self.check_in_guest()
288 def test_post_launch_set_up(self):
289 self.set_up_shared_dir()
290 self.set_up_virtiofs()
291 self.launch_vm()
293 self.set_up_nested_mounts()
295 self.mount_in_guest()
296 self.check_in_guest()
298 def test_post_mount_set_up(self):
299 self.set_up_shared_dir()
300 self.set_up_virtiofs()
301 self.launch_vm()
302 self.mount_in_guest()
304 self.set_up_nested_mounts()
306 self.check_in_guest()
308 def test_two_runs(self):
309 self.set_up_shared_dir()
311 self.set_up_nested_mounts()
313 self.set_up_virtiofs()
314 self.launch_vm()
315 self.mount_in_guest()
316 self.check_in_guest()
318 self.live_cleanup()
319 self.set_up_nested_mounts()
321 self.check_in_guest()