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
18 subp
= subprocess
.Popen(args
,
19 stdout
=subprocess
.PIPE
,
20 stderr
=subprocess
.PIPE
,
21 universal_newlines
=True)
22 stdout
, stderr
= subp
.communicate()
25 return (stdout
, stderr
, ret
)
27 def has_cmd(name
, args
=None):
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):
37 args
= ('which', name
)
40 _
, stderr
, exitcode
= run_cmd(args
)
41 except Exception as e
:
46 cmd_line
= ' '.join(args
)
47 err
= f
'{name} required, but "{cmd_line}" failed: {stderr.strip()}'
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')),
59 def test_something_that_needs_cmd1_and_cmd2(self):
64 if isinstance(cmd
, str):
67 ok
, errstr
= has_cmd(*cmd
)
69 return (False, errstr
)
74 class VirtiofsSubmountsTest(BootLinux
):
76 :avocado: tags=arch:x86_64
79 def get_portfwd(self
):
82 res
= self
.vm
.command('human-monitor-command',
83 command_line
='info usernet')
84 for line
in res
.split('\r\n'):
86 re
.search(r
'TCP.HOST_FORWARD.*127\.0\.0\.1\s*(\d+)\s+10\.',
92 self
.assertIsNotNone(port
)
93 self
.log
.debug('sshd listening on port: ' + 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
)
103 self
.ssh_session
.connect()
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
)
130 cmdline
= ' '.join(args
)
132 self
.fail(f
'{cmdline}: Returned {ret}: {stderr}')
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)
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',
163 '-o', 'announce_submounts',
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])
175 self
.run(('sudo', '-n', 'chmod', 'go+rw', self
.vfsdsock
))
177 self
.vm
.add_args('-chardev',
178 f
'socket,id=vfsdsock,path={self.vfsdsock}',
180 'vhost-user-fs-pci,queue-size=1024,chardev=vfsdsock' \
183 'memory-backend-file,id=mem,size=1G,' \
184 'mem-path=/dev/shm,share=on',
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')
195 os
.mkdir(scratch_dir
)
196 except FileExistsError
:
199 args
= ['bash', self
.get_data('host.sh'), scratch_dir
]
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'))
229 vmlinuz
= self
.params
.get('vmlinuz')
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
)
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')
264 scratch_dir
= os
.path
.join(self
.shared_dir
, 'scratch')
265 self
.run(('bash', self
.get_data('cleanup.sh'), scratch_dir
),
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()
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()
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()
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()
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()
315 self
.mount_in_guest()
316 self
.check_in_guest()
319 self
.set_up_nested_mounts()
321 self
.check_in_guest()