]> git.proxmox.com Git - mirror_qemu.git/blob - tests/acceptance/virtiofs_submounts.py
tests/acceptance: Add virtiofs_submounts.py
[mirror_qemu.git] / tests / acceptance / virtiofs_submounts.py
1 import logging
2 import re
3 import os
4 import subprocess
5 import time
6
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
11
12 from qemu.accel import kvm_available
13
14 from boot_linux import BootLinux
15
16
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
24
25 return (stdout, stderr, ret)
26
27 def has_passwordless_sudo():
28 """
29 This function is for use in a @avocado.skipUnless decorator, e.g.:
30
31 @skipUnless(*has_passwordless_sudo())
32 def test_something_that_needs_sudo(self):
33 ...
34 """
35
36 _, stderr, exitcode = run_cmd(('sudo', '-n', 'true'))
37 if exitcode != 0:
38 return (False, f'Failed to use sudo -n: {stderr.strip()}')
39 else:
40 return (True, '')
41
42
43 class VirtiofsSubmountsTest(BootLinux):
44 """
45 :avocado: tags=arch:x86_64
46 """
47
48 def get_portfwd(self):
49 port = None
50
51 res = self.vm.command('human-monitor-command',
52 command_line='info usernet')
53 for line in res.split('\r\n'):
54 match = \
55 re.search(r'TCP.HOST_FORWARD.*127\.0\.0\.1\s*(\d+)\s+10\.',
56 line)
57 if match is not None:
58 port = match[1]
59 break
60
61 self.assertIsNotNone(port)
62 self.log.debug('sshd listening on port: ' + port)
63 return port
64
65 def ssh_connect(self, username, keyfile):
66 self.ssh_logger = logging.getLogger('ssh')
67 port = self.get_portfwd()
68 self.ssh_session = ssh.Session('127.0.0.1', port=int(port),
69 user=username, key=keyfile)
70 for i in range(10):
71 try:
72 self.ssh_session.connect()
73 return
74 except:
75 time.sleep(4)
76 pass
77 self.fail('sshd timeout')
78
79 def ssh_command(self, command):
80 self.ssh_logger.info(command)
81 result = self.ssh_session.cmd(command)
82 stdout_lines = [line.rstrip() for line
83 in result.stdout_text.splitlines()]
84 for line in stdout_lines:
85 self.ssh_logger.info(line)
86 stderr_lines = [line.rstrip() for line
87 in result.stderr_text.splitlines()]
88 for line in stderr_lines:
89 self.ssh_logger.warning(line)
90
91 self.assertEqual(result.exit_status, 0,
92 f'Guest command failed: {command}')
93 return stdout_lines, stderr_lines
94
95 def run(self, args, ignore_error=False):
96 stdout, stderr, ret = run_cmd(args)
97
98 if ret != 0:
99 cmdline = ' '.join(args)
100 if not ignore_error:
101 self.fail(f'{cmdline}: Returned {ret}: {stderr}')
102 else:
103 self.log.warn(f'{cmdline}: Returned {ret}: {stderr}')
104
105 return (stdout, stderr, ret)
106
107 def set_up_shared_dir(self):
108 atwd = os.getenv('AVOCADO_TEST_WORKDIR')
109 self.shared_dir = os.path.join(atwd, 'virtiofs-shared')
110
111 os.mkdir(self.shared_dir)
112
113 self.run(('cp', self.get_data('guest.sh'),
114 os.path.join(self.shared_dir, 'check.sh')))
115
116 self.run(('cp', self.get_data('guest-cleanup.sh'),
117 os.path.join(self.shared_dir, 'cleanup.sh')))
118
119 def set_up_virtiofs(self):
120 attmp = os.getenv('AVOCADO_TESTS_COMMON_TMPDIR')
121 self.vfsdsock = os.path.join(attmp, 'vfsdsock')
122
123 self.run(('sudo', '-n', 'rm', '-f', self.vfsdsock), ignore_error=True)
124
125 self.virtiofsd = \
126 subprocess.Popen(('sudo', '-n',
127 'tools/virtiofsd/virtiofsd',
128 f'--socket-path={self.vfsdsock}',
129 '-o', f'source={self.shared_dir}',
130 '-o', 'cache=always',
131 '-o', 'xattr',
132 '-o', 'announce_submounts',
133 '-f'),
134 stdout=subprocess.DEVNULL,
135 stderr=subprocess.PIPE,
136 universal_newlines=True)
137
138 while not os.path.exists(self.vfsdsock):
139 if self.virtiofsd.poll() is not None:
140 self.fail('virtiofsd exited prematurely: ' +
141 self.virtiofsd.communicate()[1])
142 time.sleep(0.1)
143
144 self.run(('sudo', '-n', 'chmod', 'go+rw', self.vfsdsock))
145
146 self.vm.add_args('-chardev',
147 f'socket,id=vfsdsock,path={self.vfsdsock}',
148 '-device',
149 'vhost-user-fs-pci,queue-size=1024,chardev=vfsdsock' \
150 ',tag=host',
151 '-object',
152 'memory-backend-file,id=mem,size=1G,' \
153 'mem-path=/dev/shm,share=on',
154 '-numa',
155 'node,memdev=mem')
156
157 def launch_vm(self):
158 self.launch_and_wait()
159 self.ssh_connect('root', self.ssh_key)
160
161 def set_up_nested_mounts(self):
162 scratch_dir = os.path.join(self.shared_dir, 'scratch')
163 try:
164 os.mkdir(scratch_dir)
165 except FileExistsError:
166 pass
167
168 args = ['bash', self.get_data('host.sh'), scratch_dir]
169 if self.seed:
170 args += [self.seed]
171
172 out, _, _ = self.run(args)
173 seed = re.search(r'^Seed: \d+', out)
174 self.log.info(seed[0])
175
176 def mount_in_guest(self):
177 self.ssh_command('mkdir -p /mnt/host')
178 self.ssh_command('mount -t virtiofs host /mnt/host')
179
180 def check_in_guest(self):
181 self.ssh_command('bash /mnt/host/check.sh /mnt/host/scratch/share')
182
183 def live_cleanup(self):
184 self.ssh_command('bash /mnt/host/cleanup.sh /mnt/host/scratch')
185
186 # It would be nice if the above was sufficient to make virtiofsd clear
187 # all references to the mounted directories (so they can be unmounted
188 # on the host), but unfortunately it is not. To do so, we have to
189 # resort to a remount.
190 self.ssh_command('mount -o remount /mnt/host')
191
192 scratch_dir = os.path.join(self.shared_dir, 'scratch')
193 self.run(('bash', self.get_data('cleanup.sh'), scratch_dir))
194
195 @skipUnless(*has_passwordless_sudo())
196 def setUp(self):
197 vmlinuz = self.params.get('vmlinuz')
198 if vmlinuz is None:
199 self.cancel('vmlinuz parameter not set; you must point it to a '
200 'Linux kernel binary to test (to run this test with ' \
201 'the on-image kernel, set it to an empty string)')
202
203 self.seed = self.params.get('seed')
204
205 atwd = os.getenv('AVOCADO_TEST_WORKDIR')
206 self.ssh_key = os.path.join(atwd, 'id_ed25519')
207
208 self.run(('ssh-keygen', '-t', 'ed25519', '-f', self.ssh_key))
209
210 pubkey = open(self.ssh_key + '.pub').read()
211
212 super(VirtiofsSubmountsTest, self).setUp(pubkey)
213
214 if len(vmlinuz) > 0:
215 self.vm.add_args('-kernel', vmlinuz,
216 '-append', 'console=ttyS0 root=/dev/sda1')
217
218 # Allow us to connect to SSH
219 self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22',
220 '-device', 'e1000,netdev=vnet')
221
222 if not kvm_available(self.arch, self.qemu_bin):
223 self.cancel(KVM_NOT_AVAILABLE)
224 self.vm.add_args('-accel', 'kvm')
225
226 def tearDown(self):
227 try:
228 self.vm.shutdown()
229 except:
230 pass
231
232 scratch_dir = os.path.join(self.shared_dir, 'scratch')
233 self.run(('bash', self.get_data('cleanup.sh'), scratch_dir),
234 ignore_error=True)
235
236 def test_pre_virtiofsd_set_up(self):
237 self.set_up_shared_dir()
238
239 self.set_up_nested_mounts()
240
241 self.set_up_virtiofs()
242 self.launch_vm()
243 self.mount_in_guest()
244 self.check_in_guest()
245
246 def test_pre_launch_set_up(self):
247 self.set_up_shared_dir()
248 self.set_up_virtiofs()
249
250 self.set_up_nested_mounts()
251
252 self.launch_vm()
253 self.mount_in_guest()
254 self.check_in_guest()
255
256 def test_post_launch_set_up(self):
257 self.set_up_shared_dir()
258 self.set_up_virtiofs()
259 self.launch_vm()
260
261 self.set_up_nested_mounts()
262
263 self.mount_in_guest()
264 self.check_in_guest()
265
266 def test_post_mount_set_up(self):
267 self.set_up_shared_dir()
268 self.set_up_virtiofs()
269 self.launch_vm()
270 self.mount_in_guest()
271
272 self.set_up_nested_mounts()
273
274 self.check_in_guest()
275
276 def test_two_runs(self):
277 self.set_up_shared_dir()
278
279 self.set_up_nested_mounts()
280
281 self.set_up_virtiofs()
282 self.launch_vm()
283 self.mount_in_guest()
284 self.check_in_guest()
285
286 self.live_cleanup()
287 self.set_up_nested_mounts()
288
289 self.check_in_guest()