1import logging 2import re 3import os 4import subprocess 5import time 6 7from avocado import skipUnless 8from avocado_qemu import LinuxTest, BUILD_DIR 9from avocado_qemu import has_cmds 10from avocado_qemu import run_cmd 11from avocado_qemu import wait_for_console_pattern 12from avocado.utils import ssh 13 14 15class VirtiofsSubmountsTest(LinuxTest): 16 """ 17 :avocado: tags=arch:x86_64 18 :avocado: tags=accel:kvm 19 """ 20 21 def run(self, args, ignore_error=False): 22 stdout, stderr, ret = run_cmd(args) 23 24 if ret != 0: 25 cmdline = ' '.join(args) 26 if not ignore_error: 27 self.fail(f'{cmdline}: Returned {ret}: {stderr}') 28 else: 29 self.log.warn(f'{cmdline}: Returned {ret}: {stderr}') 30 31 return (stdout, stderr, ret) 32 33 def set_up_shared_dir(self): 34 self.shared_dir = os.path.join(self.workdir, 'virtiofs-shared') 35 36 os.mkdir(self.shared_dir) 37 38 self.run(('cp', self.get_data('guest.sh'), 39 os.path.join(self.shared_dir, 'check.sh'))) 40 41 self.run(('cp', self.get_data('guest-cleanup.sh'), 42 os.path.join(self.shared_dir, 'cleanup.sh'))) 43 44 def set_up_virtiofs(self): 45 attmp = os.getenv('AVOCADO_TESTS_COMMON_TMPDIR') 46 self.vfsdsock = os.path.join(attmp, 'vfsdsock') 47 48 self.run(('sudo', '-n', 'rm', '-f', self.vfsdsock), ignore_error=True) 49 50 self.virtiofsd = \ 51 subprocess.Popen(('sudo', '-n', 52 'tools/virtiofsd/virtiofsd', 53 f'--socket-path={self.vfsdsock}', 54 '-o', f'source={self.shared_dir}', 55 '-o', 'cache=always', 56 '-o', 'xattr', 57 '-o', 'announce_submounts', 58 '-f'), 59 stdout=subprocess.DEVNULL, 60 stderr=subprocess.PIPE, 61 universal_newlines=True) 62 63 while not os.path.exists(self.vfsdsock): 64 if self.virtiofsd.poll() is not None: 65 self.fail('virtiofsd exited prematurely: ' + 66 self.virtiofsd.communicate()[1]) 67 time.sleep(0.1) 68 69 self.run(('sudo', '-n', 'chmod', 'go+rw', self.vfsdsock)) 70 71 self.vm.add_args('-chardev', 72 f'socket,id=vfsdsock,path={self.vfsdsock}', 73 '-device', 74 'vhost-user-fs-pci,queue-size=1024,chardev=vfsdsock' \ 75 ',tag=host', 76 '-object', 77 'memory-backend-file,id=mem,size=1G,' \ 78 'mem-path=/dev/shm,share=on', 79 '-numa', 80 'node,memdev=mem') 81 82 def set_up_nested_mounts(self): 83 scratch_dir = os.path.join(self.shared_dir, 'scratch') 84 try: 85 os.mkdir(scratch_dir) 86 except FileExistsError: 87 pass 88 89 args = ['bash', self.get_data('host.sh'), scratch_dir] 90 if self.seed: 91 args += [self.seed] 92 93 out, _, _ = self.run(args) 94 seed = re.search(r'^Seed: \d+', out) 95 self.log.info(seed[0]) 96 97 def mount_in_guest(self): 98 self.ssh_command('mkdir -p /mnt/host') 99 self.ssh_command('mount -t virtiofs host /mnt/host') 100 101 def check_in_guest(self): 102 self.ssh_command('bash /mnt/host/check.sh /mnt/host/scratch/share') 103 104 def live_cleanup(self): 105 self.ssh_command('bash /mnt/host/cleanup.sh /mnt/host/scratch') 106 107 # It would be nice if the above was sufficient to make virtiofsd clear 108 # all references to the mounted directories (so they can be unmounted 109 # on the host), but unfortunately it is not. To do so, we have to 110 # resort to a remount. 111 self.ssh_command('mount -o remount /mnt/host') 112 113 scratch_dir = os.path.join(self.shared_dir, 'scratch') 114 self.run(('bash', self.get_data('cleanup.sh'), scratch_dir)) 115 116 @skipUnless(*has_cmds(('sudo -n', ('sudo', '-n', 'true')), 117 'ssh-keygen', 'bash', 'losetup', 'mkfs.xfs', 'mount')) 118 def setUp(self): 119 vmlinuz = self.params.get('vmlinuz') 120 if vmlinuz is None: 121 """ 122 The Linux kernel supports FUSE auto-submounts only as of 5.10. 123 boot_linux.py currently provides Fedora 31, whose kernel is too 124 old, so this test cannot pass with the on-image kernel (you are 125 welcome to try, hence the option to force such a test with 126 -p vmlinuz=''). Therefore, for now the user must provide a 127 sufficiently new custom kernel, or effectively explicitly 128 request failure with -p vmlinuz=''. 129 Once an image with a sufficiently new kernel is available 130 (probably Fedora 34), we can make -p vmlinuz='' the default, so 131 that this parameter no longer needs to be specified. 132 """ 133 self.cancel('vmlinuz parameter not set; you must point it to a ' 134 'Linux kernel binary to test (to run this test with ' \ 135 'the on-image kernel, set it to an empty string)') 136 137 self.seed = self.params.get('seed') 138 139 self.ssh_key = os.path.join(self.workdir, 'id_ed25519') 140 141 self.run(('ssh-keygen', '-N', '', '-t', 'ed25519', '-f', self.ssh_key)) 142 143 pubkey = self.ssh_key + '.pub' 144 145 super(VirtiofsSubmountsTest, self).setUp(pubkey) 146 147 if vmlinuz: 148 self.vm.add_args('-kernel', vmlinuz, 149 '-append', 'console=ttyS0 root=/dev/sda1') 150 151 self.require_accelerator("kvm") 152 self.vm.add_args('-accel', 'kvm') 153 154 def tearDown(self): 155 try: 156 self.vm.shutdown() 157 except: 158 pass 159 160 scratch_dir = os.path.join(self.shared_dir, 'scratch') 161 self.run(('bash', self.get_data('cleanup.sh'), scratch_dir), 162 ignore_error=True) 163 164 def test_pre_virtiofsd_set_up(self): 165 self.set_up_shared_dir() 166 167 self.set_up_nested_mounts() 168 169 self.set_up_virtiofs() 170 self.launch_and_wait() 171 self.mount_in_guest() 172 self.check_in_guest() 173 174 def test_pre_launch_set_up(self): 175 self.set_up_shared_dir() 176 self.set_up_virtiofs() 177 178 self.set_up_nested_mounts() 179 180 self.launch_and_wait() 181 self.mount_in_guest() 182 self.check_in_guest() 183 184 def test_post_launch_set_up(self): 185 self.set_up_shared_dir() 186 self.set_up_virtiofs() 187 self.launch_and_wait() 188 189 self.set_up_nested_mounts() 190 191 self.mount_in_guest() 192 self.check_in_guest() 193 194 def test_post_mount_set_up(self): 195 self.set_up_shared_dir() 196 self.set_up_virtiofs() 197 self.launch_and_wait() 198 self.mount_in_guest() 199 200 self.set_up_nested_mounts() 201 202 self.check_in_guest() 203 204 def test_two_runs(self): 205 self.set_up_shared_dir() 206 207 self.set_up_nested_mounts() 208 209 self.set_up_virtiofs() 210 self.launch_and_wait() 211 self.mount_in_guest() 212 self.check_in_guest() 213 214 self.live_cleanup() 215 self.set_up_nested_mounts() 216 217 self.check_in_guest() 218