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 self
.shared_dir
= os
.path
.join(self
.workdir
, 'virtiofs-shared')
141 os
.mkdir(self
.shared_dir
)
143 self
.run(('cp', self
.get_data('guest.sh'),
144 os
.path
.join(self
.shared_dir
, 'check.sh')))
146 self
.run(('cp', self
.get_data('guest-cleanup.sh'),
147 os
.path
.join(self
.shared_dir
, 'cleanup.sh')))
149 def set_up_virtiofs(self
):
150 attmp
= os
.getenv('AVOCADO_TESTS_COMMON_TMPDIR')
151 self
.vfsdsock
= os
.path
.join(attmp
, 'vfsdsock')
153 self
.run(('sudo', '-n', 'rm', '-f', self
.vfsdsock
), ignore_error
=True)
156 subprocess
.Popen(('sudo', '-n',
157 'tools/virtiofsd/virtiofsd',
158 f
'--socket-path={self.vfsdsock}',
159 '-o', f
'source={self.shared_dir}',
160 '-o', 'cache=always',
162 '-o', 'announce_submounts',
164 stdout
=subprocess
.DEVNULL
,
165 stderr
=subprocess
.PIPE
,
166 universal_newlines
=True)
168 while not os
.path
.exists(self
.vfsdsock
):
169 if self
.virtiofsd
.poll() is not None:
170 self
.fail('virtiofsd exited prematurely: ' +
171 self
.virtiofsd
.communicate()[1])
174 self
.run(('sudo', '-n', 'chmod', 'go+rw', self
.vfsdsock
))
176 self
.vm
.add_args('-chardev',
177 f
'socket,id=vfsdsock,path={self.vfsdsock}',
179 'vhost-user-fs-pci,queue-size=1024,chardev=vfsdsock' \
182 'memory-backend-file,id=mem,size=1G,' \
183 'mem-path=/dev/shm,share=on',
188 self
.launch_and_wait()
189 self
.ssh_connect('root', self
.ssh_key
)
191 def set_up_nested_mounts(self
):
192 scratch_dir
= os
.path
.join(self
.shared_dir
, 'scratch')
194 os
.mkdir(scratch_dir
)
195 except FileExistsError
:
198 args
= ['bash', self
.get_data('host.sh'), scratch_dir
]
202 out
, _
, _
= self
.run(args
)
203 seed
= re
.search(r
'^Seed: \d+', out
)
204 self
.log
.info(seed
[0])
206 def mount_in_guest(self
):
207 self
.ssh_command('mkdir -p /mnt/host')
208 self
.ssh_command('mount -t virtiofs host /mnt/host')
210 def check_in_guest(self
):
211 self
.ssh_command('bash /mnt/host/check.sh /mnt/host/scratch/share')
213 def live_cleanup(self
):
214 self
.ssh_command('bash /mnt/host/cleanup.sh /mnt/host/scratch')
216 # It would be nice if the above was sufficient to make virtiofsd clear
217 # all references to the mounted directories (so they can be unmounted
218 # on the host), but unfortunately it is not. To do so, we have to
219 # resort to a remount.
220 self
.ssh_command('mount -o remount /mnt/host')
222 scratch_dir
= os
.path
.join(self
.shared_dir
, 'scratch')
223 self
.run(('bash', self
.get_data('cleanup.sh'), scratch_dir
))
225 @skipUnless(*has_cmds(('sudo -n', ('sudo', '-n', 'true')),
226 'ssh-keygen', 'bash', 'losetup', 'mkfs.xfs', 'mount'))
228 vmlinuz
= self
.params
.get('vmlinuz')
230 self
.cancel('vmlinuz parameter not set; you must point it to a '
231 'Linux kernel binary to test (to run this test with ' \
232 'the on-image kernel, set it to an empty string)')
234 self
.seed
= self
.params
.get('seed')
236 self
.ssh_key
= os
.path
.join(self
.workdir
, 'id_ed25519')
238 self
.run(('ssh-keygen', '-N', '', '-t', 'ed25519', '-f', self
.ssh_key
))
240 pubkey
= open(self
.ssh_key
+ '.pub').read()
242 super(VirtiofsSubmountsTest
, self
).setUp(pubkey
)
245 self
.vm
.add_args('-kernel', vmlinuz
,
246 '-append', 'console=ttyS0 root=/dev/sda1')
248 # Allow us to connect to SSH
249 self
.vm
.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22',
250 '-device', 'e1000,netdev=vnet')
252 if not kvm_available(self
.arch
, self
.qemu_bin
):
253 self
.cancel(KVM_NOT_AVAILABLE
)
254 self
.vm
.add_args('-accel', 'kvm')
262 scratch_dir
= os
.path
.join(self
.shared_dir
, 'scratch')
263 self
.run(('bash', self
.get_data('cleanup.sh'), scratch_dir
),
266 def test_pre_virtiofsd_set_up(self
):
267 self
.set_up_shared_dir()
269 self
.set_up_nested_mounts()
271 self
.set_up_virtiofs()
273 self
.mount_in_guest()
274 self
.check_in_guest()
276 def test_pre_launch_set_up(self
):
277 self
.set_up_shared_dir()
278 self
.set_up_virtiofs()
280 self
.set_up_nested_mounts()
283 self
.mount_in_guest()
284 self
.check_in_guest()
286 def test_post_launch_set_up(self
):
287 self
.set_up_shared_dir()
288 self
.set_up_virtiofs()
291 self
.set_up_nested_mounts()
293 self
.mount_in_guest()
294 self
.check_in_guest()
296 def test_post_mount_set_up(self
):
297 self
.set_up_shared_dir()
298 self
.set_up_virtiofs()
300 self
.mount_in_guest()
302 self
.set_up_nested_mounts()
304 self
.check_in_guest()
306 def test_two_runs(self
):
307 self
.set_up_shared_dir()
309 self
.set_up_nested_mounts()
311 self
.set_up_virtiofs()
313 self
.mount_in_guest()
314 self
.check_in_guest()
317 self
.set_up_nested_mounts()
319 self
.check_in_guest()