xref: /openbmc/openbmc/poky/meta/lib/oeqa/selftest/cases/wic.py (revision 8460358c3d24c71d9d38fd126c745854a6301564)
1 #
2 # Copyright (c) 2015, Intel Corporation.
3 #
4 # SPDX-License-Identifier: GPL-2.0-only
5 #
6 # AUTHORS
7 # Ed Bartosh <ed.bartosh@linux.intel.com>
8 
9 """Test cases for wic."""
10 
11 import os
12 import sys
13 import unittest
14 import hashlib
15 import subprocess
16 
17 from glob import glob
18 from shutil import rmtree, copy
19 from tempfile import NamedTemporaryFile
20 from tempfile import TemporaryDirectory
21 
22 from oeqa.selftest.case import OESelftestTestCase
23 from oeqa.core.decorator import OETestTag
24 from oeqa.core.decorator.data import skipIfNotArch
25 from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars, runqemu
26 
27 
28 def extract_files(debugfs_output):
29     """
30     extract file names from the output of debugfs -R 'ls -p',
31     which looks like this:
32 
33      /2/040755/0/0/.//\n
34      /2/040755/0/0/..//\n
35      /11/040700/0/0/lost+found^M//\n
36      /12/040755/1002/1002/run//\n
37      /13/040755/1002/1002/sys//\n
38      /14/040755/1002/1002/bin//\n
39      /80/040755/1002/1002/var//\n
40      /92/040755/1002/1002/tmp//\n
41     """
42     # NOTE the occasional ^M in file names
43     return [line.split('/')[5].strip() for line in \
44             debugfs_output.strip().split('/\n')]
45 
46 def files_own_by_root(debugfs_output):
47     for line in debugfs_output.strip().split('/\n'):
48         if line.split('/')[3:5] != ['0', '0']:
49             print(debugfs_output)
50             return False
51     return True
52 
53 class WicTestCase(OESelftestTestCase):
54     """Wic test class."""
55 
56     image_is_ready = False
57     wicenv_cache = {}
58 
59     def setUpLocal(self):
60         """This code is executed before each test method."""
61         self.resultdir = os.path.join(self.builddir, "wic-tmp")
62         super(WicTestCase, self).setUpLocal()
63 
64         # Do this here instead of in setUpClass as the base setUp does some
65         # clean up which can result in the native tools built earlier in
66         # setUpClass being unavailable.
67         if not WicTestCase.image_is_ready:
68             if self.td['USE_NLS'] != 'yes':
69                 self.skipTest('wic-tools needs USE_NLS=yes')
70 
71             bitbake('wic-tools core-image-minimal core-image-minimal-mtdutils')
72             WicTestCase.image_is_ready = True
73         rmtree(self.resultdir, ignore_errors=True)
74 
75     def tearDownLocal(self):
76         """Remove resultdir as it may contain images."""
77         rmtree(self.resultdir, ignore_errors=True)
78         super(WicTestCase, self).tearDownLocal()
79 
80     def _get_image_env_path(self, image):
81         """Generate and obtain the path to <image>.env"""
82         if image not in WicTestCase.wicenv_cache:
83             bitbake('%s -c do_rootfs_wicenv' % image)
84             stdir = get_bb_var('STAGING_DIR', image)
85             machine = self.td["MACHINE"]
86             WicTestCase.wicenv_cache[image] = os.path.join(stdir, machine, 'imgdata')
87         return WicTestCase.wicenv_cache[image]
88 
89 class CLITests(OESelftestTestCase):
90     def test_version(self):
91         """Test wic --version"""
92         runCmd('wic --version')
93 
94     def test_help(self):
95         """Test wic --help and wic -h"""
96         runCmd('wic --help')
97         runCmd('wic -h')
98 
99     def test_createhelp(self):
100         """Test wic create --help"""
101         runCmd('wic create --help')
102 
103     def test_listhelp(self):
104         """Test wic list --help"""
105         runCmd('wic list --help')
106 
107     def test_help_create(self):
108         """Test wic help create"""
109         runCmd('wic help create')
110 
111     def test_help_list(self):
112         """Test wic help list"""
113         runCmd('wic help list')
114 
115     def test_help_overview(self):
116         """Test wic help overview"""
117         runCmd('wic help overview')
118 
119     def test_help_plugins(self):
120         """Test wic help plugins"""
121         runCmd('wic help plugins')
122 
123     def test_help_kickstart(self):
124         """Test wic help kickstart"""
125         runCmd('wic help kickstart')
126 
127     def test_list_images(self):
128         """Test wic list images"""
129         runCmd('wic list images')
130 
131     def test_list_source_plugins(self):
132         """Test wic list source-plugins"""
133         runCmd('wic list source-plugins')
134 
135     def test_listed_images_help(self):
136         """Test wic listed images help"""
137         output = runCmd('wic list images').output
138         imagelist = [line.split()[0] for line in output.splitlines()]
139         for image in imagelist:
140             runCmd('wic list %s help' % image)
141 
142     def test_unsupported_subcommand(self):
143         """Test unsupported subcommand"""
144         self.assertNotEqual(0, runCmd('wic unsupported', ignore_status=True).status)
145 
146     def test_no_command(self):
147         """Test wic without command"""
148         self.assertEqual(1, runCmd('wic', ignore_status=True).status)
149 
150 class Wic(WicTestCase):
151     def test_skip_kernel_install(self):
152         """Test the functionality of not installing the kernel in the boot directory using the wic plugin"""
153         # create a temporary file for the WKS content
154         with NamedTemporaryFile("w", suffix=".wks") as wks:
155             wks.write(
156                 'part --source bootimg-efi '
157                 '--sourceparams="loader=grub-efi,install-kernel-into-boot-dir=false" '
158                 '--label boot --active\n'
159             )
160             wks.flush()
161             # create a temporary directory to extract the disk image to
162             with TemporaryDirectory() as tmpdir:
163                 img = 'core-image-minimal'
164                 # build the image using the WKS file
165                 cmd = "wic create %s -e %s -o %s" % (
166                     wks.name, img, self.resultdir)
167                 runCmd(cmd)
168                 wksname = os.path.splitext(os.path.basename(wks.name))[0]
169                 out = glob(os.path.join(
170                     self.resultdir, "%s-*.direct" % wksname))
171                 self.assertEqual(1, len(out))
172                 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
173                 # extract the content of the disk image to the temporary directory
174                 cmd = "wic cp %s:1 %s -n %s" % (out[0], tmpdir, sysroot)
175                 runCmd(cmd)
176                 # check if the kernel is installed or not
177                 kimgtype = get_bb_var('KERNEL_IMAGETYPE', img)
178                 for file in os.listdir(tmpdir):
179                     if file == kimgtype:
180                         raise AssertionError(
181                             "The kernel image '{}' was found in the partition".format(kimgtype)
182                         )
183 
184     def test_kernel_install(self):
185         """Test the installation of the kernel to the boot directory in the wic plugin"""
186         # create a temporary file for the WKS content
187         with NamedTemporaryFile("w", suffix=".wks") as wks:
188             wks.write(
189                 'part --source bootimg-efi '
190                 '--sourceparams="loader=grub-efi,install-kernel-into-boot-dir=true" '
191                 '--label boot --active\n'
192             )
193             wks.flush()
194             # create a temporary directory to extract the disk image to
195             with TemporaryDirectory() as tmpdir:
196                 img = 'core-image-minimal'
197                 # build the image using the WKS file
198                 cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
199                 runCmd(cmd)
200                 wksname = os.path.splitext(os.path.basename(wks.name))[0]
201                 out = glob(os.path.join(self.resultdir, "%s-*.direct" % wksname))
202                 self.assertEqual(1, len(out))
203                 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
204                 # extract the content of the disk image to the temporary directory
205                 cmd = "wic cp %s:1 %s -n %s" % (out[0], tmpdir, sysroot)
206                 runCmd(cmd)
207                 # check if the kernel is installed or not
208                 kimgtype = get_bb_var('KERNEL_IMAGETYPE', img)
209                 found = False
210                 for file in os.listdir(tmpdir):
211                     if file == kimgtype:
212                         found = True
213                         break
214                 self.assertTrue(
215                     found, "The kernel image '{}' was not found in the boot partition".format(kimgtype)
216                 )
217 
218     def test_build_image_name(self):
219         """Test wic create wictestdisk --image-name=core-image-minimal"""
220         cmd = "wic create wictestdisk --image-name=core-image-minimal -o %s" % self.resultdir
221         runCmd(cmd)
222         self.assertEqual(1, len(glob(os.path.join (self.resultdir, "wictestdisk-*.direct"))))
223 
224     @skipIfNotArch(['i586', 'i686', 'x86_64'])
225     def test_gpt_image(self):
226         """Test creation of core-image-minimal with gpt table and UUID boot"""
227         cmd = "wic create directdisk-gpt --image-name core-image-minimal -o %s" % self.resultdir
228         runCmd(cmd)
229         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "directdisk-*.direct"))))
230 
231     @skipIfNotArch(['i586', 'i686', 'x86_64'])
232     def test_iso_image(self):
233         """Test creation of hybrid iso image with legacy and EFI boot"""
234         config = 'INITRAMFS_IMAGE = "core-image-minimal-initramfs"\n'\
235                  'MACHINE_FEATURES:append = " efi"\n'\
236                  'DEPENDS:pn-core-image-minimal += "syslinux"\n'
237         self.append_config(config)
238         bitbake('core-image-minimal core-image-minimal-initramfs')
239         self.remove_config(config)
240         cmd = "wic create mkhybridiso --image-name core-image-minimal -o %s" % self.resultdir
241         runCmd(cmd)
242         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "HYBRID_ISO_IMG-*.direct"))))
243         self.assertEqual(1, len(glob(os.path.join (self.resultdir, "HYBRID_ISO_IMG-*.iso"))))
244 
245     @skipIfNotArch(['i586', 'i686', 'x86_64'])
246     def test_qemux86_directdisk(self):
247         """Test creation of qemux-86-directdisk image"""
248         cmd = "wic create qemux86-directdisk -e core-image-minimal -o %s" % self.resultdir
249         runCmd(cmd)
250         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "qemux86-directdisk-*direct"))))
251 
252     @skipIfNotArch(['i586', 'i686', 'x86_64', 'aarch64'])
253     def test_mkefidisk(self):
254         """Test creation of mkefidisk image"""
255         cmd = "wic create mkefidisk -e core-image-minimal -o %s" % self.resultdir
256         runCmd(cmd)
257         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "mkefidisk-*direct"))))
258 
259     @skipIfNotArch(['i586', 'i686', 'x86_64'])
260     def test_bootloader_config(self):
261         """Test creation of directdisk-bootloader-config image"""
262         config = 'DEPENDS:pn-core-image-minimal += "syslinux"\n'
263         self.append_config(config)
264         bitbake('core-image-minimal')
265         self.remove_config(config)
266         cmd = "wic create directdisk-bootloader-config -e core-image-minimal -o %s" % self.resultdir
267         runCmd(cmd)
268         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "directdisk-bootloader-config-*direct"))))
269 
270     @skipIfNotArch(['i586', 'i686', 'x86_64', 'aarch64'])
271     def test_systemd_bootdisk(self):
272         """Test creation of systemd-bootdisk image"""
273         config = 'MACHINE_FEATURES:append = " efi"\n'
274         self.append_config(config)
275         bitbake('core-image-minimal')
276         self.remove_config(config)
277         cmd = "wic create systemd-bootdisk -e core-image-minimal -o %s" % self.resultdir
278         runCmd(cmd)
279         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "systemd-bootdisk-*direct"))))
280 
281     def test_efi_bootpart(self):
282         """Test creation of efi-bootpart image"""
283         cmd = "wic create mkefidisk -e core-image-minimal -o %s" % self.resultdir
284         kimgtype = get_bb_var('KERNEL_IMAGETYPE', 'core-image-minimal')
285         self.append_config('IMAGE_EFI_BOOT_FILES = "%s;kernel"\n' % kimgtype)
286         runCmd(cmd)
287         sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
288         images = glob(os.path.join(self.resultdir, "mkefidisk-*.direct"))
289         result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
290         self.assertIn("kernel",result.output)
291 
292     def test_sdimage_bootpart(self):
293         """Test creation of sdimage-bootpart image"""
294         cmd = "wic create sdimage-bootpart -e core-image-minimal -o %s" % self.resultdir
295         kimgtype = get_bb_var('KERNEL_IMAGETYPE', 'core-image-minimal')
296         self.write_config('IMAGE_BOOT_FILES = "%s"\n' % kimgtype)
297         runCmd(cmd)
298         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "sdimage-bootpart-*direct"))))
299 
300     # TODO this doesn't have to be x86-specific
301     @skipIfNotArch(['i586', 'i686', 'x86_64'])
302     def test_default_output_dir(self):
303         """Test default output location"""
304         for fname in glob("directdisk-*.direct"):
305             os.remove(fname)
306         config = 'DEPENDS:pn-core-image-minimal += "syslinux"\n'
307         self.append_config(config)
308         bitbake('core-image-minimal')
309         self.remove_config(config)
310         cmd = "wic create directdisk -e core-image-minimal"
311         runCmd(cmd)
312         self.assertEqual(1, len(glob("directdisk-*.direct")))
313 
314     @skipIfNotArch(['i586', 'i686', 'x86_64'])
315     def test_build_artifacts(self):
316         """Test wic create directdisk providing all artifacts."""
317         bb_vars = get_bb_vars(['STAGING_DATADIR', 'RECIPE_SYSROOT_NATIVE'],
318                               'wic-tools')
319         bb_vars.update(get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_ROOTFS'],
320                                    'core-image-minimal'))
321         bbvars = {key.lower(): value for key, value in bb_vars.items()}
322         bbvars['resultdir'] = self.resultdir
323         runCmd("wic create directdisk "
324                         "-b %(staging_datadir)s "
325                         "-k %(deploy_dir_image)s "
326                         "-n %(recipe_sysroot_native)s "
327                         "-r %(image_rootfs)s "
328                         "-o %(resultdir)s" % bbvars)
329         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "directdisk-*.direct"))))
330 
331     def test_compress_gzip(self):
332         """Test compressing an image with gzip"""
333         runCmd("wic create wictestdisk "
334                                    "--image-name core-image-minimal "
335                                    "-c gzip -o %s" % self.resultdir)
336         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct.gz"))))
337 
338     def test_compress_bzip2(self):
339         """Test compressing an image with bzip2"""
340         runCmd("wic create wictestdisk "
341                                    "--image-name=core-image-minimal "
342                                    "-c bzip2 -o %s" % self.resultdir)
343         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct.bz2"))))
344 
345     def test_compress_xz(self):
346         """Test compressing an image with xz"""
347         runCmd("wic create wictestdisk "
348                                    "--image-name=core-image-minimal "
349                                    "--compress-with=xz -o %s" % self.resultdir)
350         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct.xz"))))
351 
352     def test_wrong_compressor(self):
353         """Test how wic breaks if wrong compressor is provided"""
354         self.assertEqual(2, runCmd("wic create wictestdisk "
355                                    "--image-name=core-image-minimal "
356                                    "-c wrong -o %s" % self.resultdir,
357                                    ignore_status=True).status)
358 
359     def test_debug_short(self):
360         """Test -D option"""
361         runCmd("wic create wictestdisk "
362                                    "--image-name=core-image-minimal "
363                                    "-D -o %s" % self.resultdir)
364         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
365         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "tmp.wic*"))))
366 
367     def test_debug_long(self):
368         """Test --debug option"""
369         runCmd("wic create wictestdisk "
370                                    "--image-name=core-image-minimal "
371                                    "--debug -o %s" % self.resultdir)
372         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
373         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "tmp.wic*"))))
374 
375     def test_skip_build_check_short(self):
376         """Test -s option"""
377         runCmd("wic create wictestdisk "
378                                    "--image-name=core-image-minimal "
379                                    "-s -o %s" % self.resultdir)
380         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
381 
382     def test_skip_build_check_long(self):
383         """Test --skip-build-check option"""
384         runCmd("wic create wictestdisk "
385                                    "--image-name=core-image-minimal "
386                                    "--skip-build-check "
387                                    "--outdir %s" % self.resultdir)
388         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
389 
390     def test_build_rootfs_short(self):
391         """Test -f option"""
392         runCmd("wic create wictestdisk "
393                                    "--image-name=core-image-minimal "
394                                    "-f -o %s" % self.resultdir)
395         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
396 
397     def test_build_rootfs_long(self):
398         """Test --build-rootfs option"""
399         runCmd("wic create wictestdisk "
400                                    "--image-name=core-image-minimal "
401                                    "--build-rootfs "
402                                    "--outdir %s" % self.resultdir)
403         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
404 
405     # TODO this doesn't have to be x86-specific
406     @skipIfNotArch(['i586', 'i686', 'x86_64'])
407     def test_rootfs_indirect_recipes(self):
408         """Test usage of rootfs plugin with rootfs recipes"""
409         runCmd("wic create directdisk-multi-rootfs "
410                         "--image-name=core-image-minimal "
411                         "--rootfs rootfs1=core-image-minimal "
412                         "--rootfs rootfs2=core-image-minimal "
413                         "--outdir %s" % self.resultdir)
414         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "directdisk-multi-rootfs*.direct"))))
415 
416     # TODO this doesn't have to be x86-specific
417     @skipIfNotArch(['i586', 'i686', 'x86_64'])
418     def test_rootfs_artifacts(self):
419         """Test usage of rootfs plugin with rootfs paths"""
420         bb_vars = get_bb_vars(['STAGING_DATADIR', 'RECIPE_SYSROOT_NATIVE'],
421                               'wic-tools')
422         bb_vars.update(get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_ROOTFS'],
423                                    'core-image-minimal'))
424         bbvars = {key.lower(): value for key, value in bb_vars.items()}
425         bbvars['wks'] = "directdisk-multi-rootfs"
426         bbvars['resultdir'] = self.resultdir
427         runCmd("wic create %(wks)s "
428                         "--bootimg-dir=%(staging_datadir)s "
429                         "--kernel-dir=%(deploy_dir_image)s "
430                         "--native-sysroot=%(recipe_sysroot_native)s "
431                         "--rootfs-dir rootfs1=%(image_rootfs)s "
432                         "--rootfs-dir rootfs2=%(image_rootfs)s "
433                         "--outdir %(resultdir)s" % bbvars)
434         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "%(wks)s-*.direct" % bbvars))))
435 
436     def test_exclude_path(self):
437         """Test --exclude-path wks option."""
438 
439         oldpath = os.environ['PATH']
440         os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
441 
442         try:
443             wks_file = 'temp.wks'
444             with open(wks_file, 'w') as wks:
445                 rootfs_dir = get_bb_var('IMAGE_ROOTFS', 'core-image-minimal')
446                 wks.write("""
447 part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path usr
448 part /usr --source rootfs --ondisk mmcblk0 --fstype=ext4 --rootfs-dir %s/usr
449 part /etc --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path bin/ --rootfs-dir %s/usr"""
450                           % (rootfs_dir, rootfs_dir))
451             runCmd("wic create %s -e core-image-minimal -o %s" \
452                                        % (wks_file, self.resultdir))
453 
454             os.remove(wks_file)
455             wicout = glob(os.path.join(self.resultdir, "%s-*direct" % 'temp'))
456             self.assertEqual(1, len(wicout))
457 
458             wicimg = wicout[0]
459 
460             # verify partition size with wic
461             res = runCmd("parted -m %s unit b p" % wicimg, stderr=subprocess.PIPE)
462 
463             # parse parted output which looks like this:
464             # BYT;\n
465             # /var/tmp/wic/build/tmpfwvjjkf_-201611101222-hda.direct:200MiB:file:512:512:msdos::;\n
466             # 1:0.00MiB:200MiB:200MiB:ext4::;\n
467             partlns = res.output.splitlines()[2:]
468 
469             self.assertEqual(3, len(partlns))
470 
471             for part in [1, 2, 3]:
472                 part_file = os.path.join(self.resultdir, "selftest_img.part%d" % part)
473                 partln = partlns[part-1].split(":")
474                 self.assertEqual(7, len(partln))
475                 start = int(partln[1].rstrip("B")) / 512
476                 length = int(partln[3].rstrip("B")) / 512
477                 runCmd("dd if=%s of=%s skip=%d count=%d" %
478                                            (wicimg, part_file, start, length))
479 
480             # Test partition 1, should contain the normal root directories, except
481             # /usr.
482             res = runCmd("debugfs -R 'ls -p' %s" % \
483                              os.path.join(self.resultdir, "selftest_img.part1"), stderr=subprocess.PIPE)
484             files = extract_files(res.output)
485             self.assertIn("etc", files)
486             self.assertNotIn("usr", files)
487 
488             # Partition 2, should contain common directories for /usr, not root
489             # directories.
490             res = runCmd("debugfs -R 'ls -p' %s" % \
491                              os.path.join(self.resultdir, "selftest_img.part2"), stderr=subprocess.PIPE)
492             files = extract_files(res.output)
493             self.assertNotIn("etc", files)
494             self.assertNotIn("usr", files)
495             self.assertIn("share", files)
496 
497             # Partition 3, should contain the same as partition 2, including the bin
498             # directory, but not the files inside it.
499             res = runCmd("debugfs -R 'ls -p' %s" % \
500                              os.path.join(self.resultdir, "selftest_img.part3"), stderr=subprocess.PIPE)
501             files = extract_files(res.output)
502             self.assertNotIn("etc", files)
503             self.assertNotIn("usr", files)
504             self.assertIn("share", files)
505             self.assertIn("bin", files)
506             res = runCmd("debugfs -R 'ls -p bin' %s" % \
507                              os.path.join(self.resultdir, "selftest_img.part3"), stderr=subprocess.PIPE)
508             files = extract_files(res.output)
509             self.assertIn(".", files)
510             self.assertIn("..", files)
511             self.assertEqual(2, len(files))
512 
513             for part in [1, 2, 3]:
514                 part_file = os.path.join(self.resultdir, "selftest_img.part%d" % part)
515                 os.remove(part_file)
516 
517         finally:
518             os.environ['PATH'] = oldpath
519 
520     def test_include_path(self):
521         """Test --include-path wks option."""
522 
523         oldpath = os.environ['PATH']
524         os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
525 
526         try:
527             include_path = os.path.join(self.resultdir, 'test-include')
528             os.makedirs(include_path)
529             with open(os.path.join(include_path, 'test-file'), 'w') as t:
530                 t.write("test\n")
531             wks_file = os.path.join(include_path, 'temp.wks')
532             with open(wks_file, 'w') as wks:
533                 rootfs_dir = get_bb_var('IMAGE_ROOTFS', 'core-image-minimal')
534                 wks.write("""
535 part /part1 --source rootfs --ondisk mmcblk0 --fstype=ext4
536 part /part2 --source rootfs --ondisk mmcblk0 --fstype=ext4 --include-path %s"""
537                           % (include_path))
538             runCmd("wic create %s -e core-image-minimal -o %s" \
539                                        % (wks_file, self.resultdir))
540 
541             part1 = glob(os.path.join(self.resultdir, 'temp-*.direct.p1'))[0]
542             part2 = glob(os.path.join(self.resultdir, 'temp-*.direct.p2'))[0]
543 
544             # Test partition 1, should not contain 'test-file'
545             res = runCmd("debugfs -R 'ls -p' %s" % (part1), stderr=subprocess.PIPE)
546             files = extract_files(res.output)
547             self.assertNotIn('test-file', files)
548             self.assertEqual(True, files_own_by_root(res.output))
549 
550             # Test partition 2, should contain 'test-file'
551             res = runCmd("debugfs -R 'ls -p' %s" % (part2), stderr=subprocess.PIPE)
552             files = extract_files(res.output)
553             self.assertIn('test-file', files)
554             self.assertEqual(True, files_own_by_root(res.output))
555 
556         finally:
557             os.environ['PATH'] = oldpath
558 
559     def test_include_path_embeded(self):
560         """Test --include-path wks option."""
561 
562         oldpath = os.environ['PATH']
563         os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
564 
565         try:
566             include_path = os.path.join(self.resultdir, 'test-include')
567             os.makedirs(include_path)
568             with open(os.path.join(include_path, 'test-file'), 'w') as t:
569                 t.write("test\n")
570             wks_file = os.path.join(include_path, 'temp.wks')
571             with open(wks_file, 'w') as wks:
572                 wks.write("""
573 part / --source rootfs  --fstype=ext4 --include-path %s --include-path core-image-minimal-mtdutils export/"""
574                           % (include_path))
575             runCmd("wic create %s -e core-image-minimal -o %s" \
576                                        % (wks_file, self.resultdir))
577 
578             part1 = glob(os.path.join(self.resultdir, 'temp-*.direct.p1'))[0]
579 
580             res = runCmd("debugfs -R 'ls -p' %s" % (part1), stderr=subprocess.PIPE)
581             files = extract_files(res.output)
582             self.assertIn('test-file', files)
583             self.assertEqual(True, files_own_by_root(res.output))
584 
585             res = runCmd("debugfs -R 'ls -p /export/etc/' %s" % (part1), stderr=subprocess.PIPE)
586             files = extract_files(res.output)
587             self.assertIn('passwd', files)
588             self.assertEqual(True, files_own_by_root(res.output))
589 
590         finally:
591             os.environ['PATH'] = oldpath
592 
593     def test_include_path_errors(self):
594         """Test --include-path wks option error handling."""
595         wks_file = 'temp.wks'
596 
597         # Absolute argument.
598         with open(wks_file, 'w') as wks:
599             wks.write("part / --source rootfs --fstype=ext4 --include-path core-image-minimal-mtdutils /export")
600         self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
601                                       % (wks_file, self.resultdir), ignore_status=True).status)
602         os.remove(wks_file)
603 
604         # Argument pointing to parent directory.
605         with open(wks_file, 'w') as wks:
606             wks.write("part / --source rootfs --fstype=ext4 --include-path core-image-minimal-mtdutils ././..")
607         self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
608                                       % (wks_file, self.resultdir), ignore_status=True).status)
609         os.remove(wks_file)
610 
611         # 3 Argument pointing to parent directory.
612         with open(wks_file, 'w') as wks:
613             wks.write("part / --source rootfs --fstype=ext4 --include-path core-image-minimal-mtdutils export/ dummy")
614         self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
615                                       % (wks_file, self.resultdir), ignore_status=True).status)
616         os.remove(wks_file)
617 
618     def test_exclude_path_errors(self):
619         """Test --exclude-path wks option error handling."""
620         wks_file = 'temp.wks'
621 
622         # Absolute argument.
623         with open(wks_file, 'w') as wks:
624             wks.write("part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path /usr")
625         self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
626                                       % (wks_file, self.resultdir), ignore_status=True).status)
627         os.remove(wks_file)
628 
629         # Argument pointing to parent directory.
630         with open(wks_file, 'w') as wks:
631             wks.write("part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path ././..")
632         self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
633                                       % (wks_file, self.resultdir), ignore_status=True).status)
634         os.remove(wks_file)
635 
636     def test_permissions(self):
637         """Test permissions are respected"""
638 
639         # prepare wicenv and rootfs
640         bitbake('core-image-minimal core-image-minimal-mtdutils -c do_rootfs_wicenv')
641 
642         oldpath = os.environ['PATH']
643         os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
644 
645         t_normal = """
646 part / --source rootfs --fstype=ext4
647 """
648         t_exclude = """
649 part / --source rootfs --fstype=ext4 --exclude-path=home
650 """
651         t_multi = """
652 part / --source rootfs --ondisk sda --fstype=ext4
653 part /export --source rootfs --rootfs=core-image-minimal-mtdutils --fstype=ext4
654 """
655         t_change = """
656 part / --source rootfs --ondisk sda --fstype=ext4 --exclude-path=etc/   
657 part /etc --source rootfs --fstype=ext4 --change-directory=etc
658 """
659         tests = [t_normal, t_exclude, t_multi, t_change]
660 
661         try:
662             for test in tests:
663                 include_path = os.path.join(self.resultdir, 'test-include')
664                 os.makedirs(include_path)
665                 wks_file = os.path.join(include_path, 'temp.wks')
666                 with open(wks_file, 'w') as wks:
667                     wks.write(test)
668                 runCmd("wic create %s -e core-image-minimal -o %s" \
669                                        % (wks_file, self.resultdir))
670 
671                 for part in glob(os.path.join(self.resultdir, 'temp-*.direct.p*')):
672                     res = runCmd("debugfs -R 'ls -p' %s" % (part), stderr=subprocess.PIPE)
673                     self.assertEqual(True, files_own_by_root(res.output))
674 
675                 config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "%s"\n' % wks_file
676                 self.append_config(config)
677                 bitbake('core-image-minimal')
678                 tmpdir = os.path.join(get_bb_var('WORKDIR', 'core-image-minimal'),'build-wic')
679 
680                 # check each partition for permission
681                 for part in glob(os.path.join(tmpdir, 'temp-*.direct.p*')):
682                     res = runCmd("debugfs -R 'ls -p' %s" % (part), stderr=subprocess.PIPE)
683                     self.assertTrue(files_own_by_root(res.output)
684                         ,msg='Files permission incorrect using wks set "%s"' % test)
685 
686                 # clean config and result directory for next cases
687                 self.remove_config(config)
688                 rmtree(self.resultdir, ignore_errors=True)
689 
690         finally:
691             os.environ['PATH'] = oldpath
692 
693     def test_change_directory(self):
694         """Test --change-directory wks option."""
695 
696         oldpath = os.environ['PATH']
697         os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
698 
699         try:
700             include_path = os.path.join(self.resultdir, 'test-include')
701             os.makedirs(include_path)
702             wks_file = os.path.join(include_path, 'temp.wks')
703             with open(wks_file, 'w') as wks:
704                 wks.write("part /etc --source rootfs --fstype=ext4 --change-directory=etc")
705             runCmd("wic create %s -e core-image-minimal -o %s" \
706                                        % (wks_file, self.resultdir))
707 
708             part1 = glob(os.path.join(self.resultdir, 'temp-*.direct.p1'))[0]
709 
710             res = runCmd("debugfs -R 'ls -p' %s" % (part1), stderr=subprocess.PIPE)
711             files = extract_files(res.output)
712             self.assertIn('passwd', files)
713 
714         finally:
715             os.environ['PATH'] = oldpath
716 
717     def test_change_directory_errors(self):
718         """Test --change-directory wks option error handling."""
719         wks_file = 'temp.wks'
720 
721         # Absolute argument.
722         with open(wks_file, 'w') as wks:
723             wks.write("part / --source rootfs --fstype=ext4 --change-directory /usr")
724         self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
725                                       % (wks_file, self.resultdir), ignore_status=True).status)
726         os.remove(wks_file)
727 
728         # Argument pointing to parent directory.
729         with open(wks_file, 'w') as wks:
730             wks.write("part / --source rootfs --fstype=ext4 --change-directory ././..")
731         self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
732                                       % (wks_file, self.resultdir), ignore_status=True).status)
733         os.remove(wks_file)
734 
735     def test_no_fstab_update(self):
736         """Test --no-fstab-update wks option."""
737 
738         oldpath = os.environ['PATH']
739         os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
740 
741         # Get stock fstab from base-files recipe
742         bitbake('base-files -c do_install')
743         bf_fstab = os.path.join(get_bb_var('D', 'base-files'), 'etc', 'fstab')
744         self.assertEqual(True, os.path.exists(bf_fstab))
745         bf_fstab_md5sum = runCmd('md5sum %s ' % bf_fstab).output.split(" ")[0]
746 
747         try:
748             no_fstab_update_path = os.path.join(self.resultdir, 'test-no-fstab-update')
749             os.makedirs(no_fstab_update_path)
750             wks_file = os.path.join(no_fstab_update_path, 'temp.wks')
751             with open(wks_file, 'w') as wks:
752                 wks.writelines(['part / --source rootfs --fstype=ext4 --label rootfs\n',
753                                 'part /mnt/p2 --source rootfs --rootfs-dir=core-image-minimal ',
754                                 '--fstype=ext4 --label p2 --no-fstab-update\n'])
755             runCmd("wic create %s -e core-image-minimal -o %s" \
756                                        % (wks_file, self.resultdir))
757 
758             part_fstab_md5sum = []
759             for i in range(1, 3):
760                 part = glob(os.path.join(self.resultdir, 'temp-*.direct.p') + str(i))[0]
761                 part_fstab = runCmd("debugfs -R 'cat etc/fstab' %s" % (part), stderr=subprocess.PIPE)
762                 part_fstab_md5sum.append(hashlib.md5((part_fstab.output + "\n\n").encode('utf-8')).hexdigest())
763 
764             # '/etc/fstab' in partition 2 should contain the same stock fstab file
765             # as the one installed by the base-file recipe.
766             self.assertEqual(bf_fstab_md5sum, part_fstab_md5sum[1])
767 
768             # '/etc/fstab' in partition 1 should contain an updated fstab file.
769             self.assertNotEqual(bf_fstab_md5sum, part_fstab_md5sum[0])
770 
771         finally:
772             os.environ['PATH'] = oldpath
773 
774     def test_no_fstab_update_errors(self):
775         """Test --no-fstab-update wks option error handling."""
776         wks_file = 'temp.wks'
777 
778         # Absolute argument.
779         with open(wks_file, 'w') as wks:
780             wks.write("part / --source rootfs --fstype=ext4 --no-fstab-update /etc")
781         self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
782                                       % (wks_file, self.resultdir), ignore_status=True).status)
783         os.remove(wks_file)
784 
785         # Argument pointing to parent directory.
786         with open(wks_file, 'w') as wks:
787             wks.write("part / --source rootfs --fstype=ext4 --no-fstab-update ././..")
788         self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
789                                       % (wks_file, self.resultdir), ignore_status=True).status)
790         os.remove(wks_file)
791 
792     def test_extra_space(self):
793         """Test --extra-space wks option."""
794         extraspace = 1024**3
795         runCmd("wic create wictestdisk "
796                                    "--image-name core-image-minimal "
797                                    "--extra-space %i -o %s" % (extraspace ,self.resultdir))
798         wicout = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
799         self.assertEqual(1, len(wicout))
800         size = os.path.getsize(wicout[0])
801         self.assertTrue(size > extraspace, msg="Extra space not present (%s vs %s)" % (size, extraspace))
802 
803     def test_no_table(self):
804         """Test --no-table wks option."""
805         wks_file = 'temp.wks'
806 
807         # Absolute argument.
808         with open(wks_file, 'w') as wks:
809             wks.write("part testspace --no-table --fixed-size 16k --offset 4080k")
810         runCmd("wic create %s --image-name core-image-minimal -o %s" % (wks_file, self.resultdir))
811 
812         wicout = glob(os.path.join(self.resultdir, "*.*"))
813 
814         self.assertEqual(1, len(wicout))
815         size = os.path.getsize(wicout[0])
816         self.assertEqual(size, 4 * 1024 * 1024)
817 
818         os.remove(wks_file)
819 
820     def test_partition_hidden_attributes(self):
821         """Test --hidden wks option."""
822         wks_file = 'temp.wks'
823         sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
824         try:
825             with open(wks_file, 'w') as wks:
826                 wks.write("""
827 part / --source rootfs --fstype=ext4
828 part / --source rootfs --fstype=ext4 --hidden
829 bootloader --ptable gpt""")
830 
831             runCmd("wic create %s -e core-image-minimal -o %s" \
832                                        % (wks_file, self.resultdir))
833             wicout = os.path.join(self.resultdir, "*.direct")
834 
835             result = runCmd("%s/usr/sbin/sfdisk --part-attrs %s 1" % (sysroot, wicout))
836             self.assertEqual('', result.output)
837             result = runCmd("%s/usr/sbin/sfdisk --part-attrs %s 2" % (sysroot, wicout))
838             self.assertEqual('RequiredPartition', result.output)
839 
840         finally:
841             os.remove(wks_file)
842 
843     def test_wic_sector_size(self):
844         """Test generation image sector size"""
845 
846         oldpath = os.environ['PATH']
847         os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
848 
849         try:
850             # Add WIC_SECTOR_SIZE into config
851             config = 'WIC_SECTOR_SIZE = "4096"\n'\
852                      'WICVARS:append = " WIC_SECTOR_SIZE"\n'
853             self.append_config(config)
854             bitbake('core-image-minimal')
855 
856             # Check WIC_SECTOR_SIZE apply to bitbake variable
857             wic_sector_size_str = get_bb_var('WIC_SECTOR_SIZE', 'core-image-minimal')
858             wic_sector_size = int(wic_sector_size_str)
859             self.assertEqual(4096, wic_sector_size)
860 
861             self.logger.info("Test wic_sector_size: %d \n" % wic_sector_size)
862 
863             with NamedTemporaryFile("w", suffix=".wks") as wks:
864                 wks.writelines(
865                     ['bootloader --ptable gpt\n',
866                      'part --fstype ext4 --source rootfs --label rofs-a --mkfs-extraopts "-b 4096"\n',
867                      'part --fstype ext4 --source rootfs --use-uuid --mkfs-extraopts "-b 4096"\n'])
868                 wks.flush()
869                 cmd = "wic create %s -e core-image-minimal -o %s" % (wks.name, self.resultdir)
870                 runCmd(cmd)
871                 wksname = os.path.splitext(os.path.basename(wks.name))[0]
872                 images = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
873                 self.assertEqual(1, len(images))
874 
875             sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
876             # list partitions
877             result = runCmd("wic ls %s -n %s" % (images[0], sysroot))
878             self.assertEqual(3, len(result.output.split('\n')))
879 
880             # verify partition size with wic
881             res = runCmd("export PARTED_SECTOR_SIZE=%d; parted -m %s unit b p" % (wic_sector_size, images[0]),
882                          stderr=subprocess.PIPE)
883 
884             # parse parted output which looks like this:
885             # BYT;\n
886             # /var/tmp/wic/build/tmpgjzzefdd-202410281021-sda.direct:78569472B:file:4096:4096:gpt::;\n
887             # 1:139264B:39284735B:39145472B:ext4:rofs-a:;\n
888             # 2:39284736B:78430207B:39145472B:ext4:primary:;\n
889             disk_info = res.output.splitlines()[1]
890             # Check sector sizes
891             sector_size_logical = int(disk_info.split(":")[3])
892             sector_size_physical = int(disk_info.split(":")[4])
893             self.assertEqual(wic_sector_size, sector_size_logical, "Logical sector size is not %d." % wic_sector_size)
894             self.assertEqual(wic_sector_size, sector_size_physical, "Physical sector size is not %d." % wic_sector_size)
895 
896         finally:
897             os.environ['PATH'] = oldpath
898 
899 class Wic2(WicTestCase):
900 
901     def test_bmap_short(self):
902         """Test generation of .bmap file -m option"""
903         cmd = "wic create wictestdisk -e core-image-minimal -m -o %s" % self.resultdir
904         runCmd(cmd)
905         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct"))))
906         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct.bmap"))))
907 
908     def test_bmap_long(self):
909         """Test generation of .bmap file --bmap option"""
910         cmd = "wic create wictestdisk -e core-image-minimal --bmap -o %s" % self.resultdir
911         runCmd(cmd)
912         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct"))))
913         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct.bmap"))))
914 
915     def test_image_env(self):
916         """Test generation of <image>.env files."""
917         image = 'core-image-minimal'
918         imgdatadir = self._get_image_env_path(image)
919 
920         bb_vars = get_bb_vars(['IMAGE_BASENAME', 'WICVARS'], image)
921         basename = bb_vars['IMAGE_BASENAME']
922         self.assertEqual(basename, image)
923         path = os.path.join(imgdatadir, basename) + '.env'
924         self.assertTrue(os.path.isfile(path), msg="File %s wasn't generated as expected" % path)
925 
926         wicvars = set(bb_vars['WICVARS'].split())
927         # filter out optional variables
928         wicvars = wicvars.difference(('DEPLOY_DIR_IMAGE', 'IMAGE_BOOT_FILES',
929                                       'INITRD', 'INITRD_LIVE', 'ISODIR','INITRAMFS_IMAGE',
930                                       'INITRAMFS_IMAGE_BUNDLE', 'INITRAMFS_LINK_NAME',
931                                       'APPEND', 'IMAGE_EFI_BOOT_FILES'))
932         with open(path) as envfile:
933             content = dict(line.split("=", 1) for line in envfile)
934             # test if variables used by wic present in the .env file
935             for var in wicvars:
936                 self.assertTrue(var in content, "%s is not in .env file" % var)
937                 self.assertTrue(content[var], "%s doesn't have a value (%s)" % (var, content[var]))
938 
939     def test_image_vars_dir_short(self):
940         """Test image vars directory selection -v option"""
941         image = 'core-image-minimal'
942         imgenvdir = self._get_image_env_path(image)
943         native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
944 
945         runCmd("wic create wictestdisk "
946                                    "--image-name=%s -v %s -n %s -o %s"
947                                    % (image, imgenvdir, native_sysroot,
948                                       self.resultdir))
949         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct"))))
950 
951     def test_image_vars_dir_long(self):
952         """Test image vars directory selection --vars option"""
953         image = 'core-image-minimal'
954         imgenvdir = self._get_image_env_path(image)
955         native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
956 
957         runCmd("wic create wictestdisk "
958                                    "--image-name=%s "
959                                    "--vars %s "
960                                    "--native-sysroot %s "
961                                    "--outdir %s"
962                                    % (image, imgenvdir, native_sysroot,
963                                       self.resultdir))
964         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct"))))
965 
966     # TODO this test could also work on aarch64
967     @skipIfNotArch(['i586', 'i686', 'x86_64'])
968     def test_wic_image_type(self):
969         """Test building wic images by bitbake"""
970         config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "wic-image-minimal"\n'\
971                  'MACHINE_FEATURES:append = " efi"\n'
972         self.append_config(config)
973         image = 'wic-image-minimal'
974         bitbake(image)
975         self.remove_config(config)
976 
977         bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image)
978         prefix = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], '%s.' % bb_vars['IMAGE_LINK_NAME'])
979 
980         # check if we have result image and manifests symlinks
981         # pointing to existing files
982         for suffix in ('wic', 'manifest'):
983             path = prefix + suffix
984             self.assertTrue(os.path.islink(path), msg="Link %s wasn't generated as expected" % path)
985             self.assertTrue(os.path.isfile(os.path.realpath(path)), msg="File linked to by %s wasn't generated as expected" % path)
986 
987     # TODO this should work on aarch64
988     @skipIfNotArch(['i586', 'i686', 'x86_64'])
989     @OETestTag("runqemu")
990     def test_qemu(self):
991         """Test wic-image-minimal under qemu"""
992         config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "wic-image-minimal"\n'\
993                  'MACHINE_FEATURES:append = " efi"\n'
994         self.append_config(config)
995         bitbake('wic-image-minimal')
996         self.remove_config(config)
997 
998         runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'wic-image-minimal') or ""
999         with runqemu('wic-image-minimal', ssh=False, runqemuparams='%s nographic' % (runqemu_params)) as qemu:
1000             cmd = "mount | grep '^/dev/' | cut -f1,3 -d ' ' | egrep -c -e '/dev/sda1 /boot' " \
1001                   "-e '/dev/root /|/dev/sda2 /' -e '/dev/sda3 /media' -e '/dev/sda4 /mnt'"
1002             status, output = qemu.run_serial(cmd)
1003             self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1004             self.assertEqual(output, '4')
1005             cmd = "grep UUID=2c71ef06-a81d-4735-9d3a-379b69c6bdba /etc/fstab"
1006             status, output = qemu.run_serial(cmd)
1007             self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1008             self.assertEqual(output, 'UUID=2c71ef06-a81d-4735-9d3a-379b69c6bdba\t/media\text4\tdefaults\t0\t0')
1009 
1010     @skipIfNotArch(['i586', 'i686', 'x86_64'])
1011     @OETestTag("runqemu")
1012     def test_qemu_efi(self):
1013         """Test core-image-minimal efi image under qemu"""
1014         config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "mkefidisk.wks"\n'
1015         self.append_config(config)
1016         bitbake('core-image-minimal ovmf')
1017         self.remove_config(config)
1018 
1019         runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-minimal') or ""
1020         with runqemu('core-image-minimal', ssh=False,
1021                      runqemuparams='%s nographic ovmf' % (runqemu_params), image_fstype='wic') as qemu:
1022             cmd = "grep sda. /proc/partitions  |wc -l"
1023             status, output = qemu.run_serial(cmd)
1024             self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1025             self.assertEqual(output, '3')
1026 
1027     @staticmethod
1028     def _make_fixed_size_wks(size):
1029         """
1030         Create a wks of an image with a single partition. Size of the partition is set
1031         using --fixed-size flag. Returns a tuple: (path to wks file, wks image name)
1032         """
1033         with NamedTemporaryFile("w", suffix=".wks", delete=False) as tempf:
1034             wkspath = tempf.name
1035             tempf.write("part " \
1036                      "--source rootfs --ondisk hda --align 4 --fixed-size %d "
1037                      "--fstype=ext4\n" % size)
1038 
1039         return wkspath
1040 
1041     def _get_wic_partitions(self, wkspath, native_sysroot=None, ignore_status=False):
1042         p = runCmd("wic create %s -e core-image-minimal -o %s" % (wkspath, self.resultdir),
1043                    ignore_status=ignore_status)
1044 
1045         if p.status:
1046             return (p, None)
1047 
1048         wksname = os.path.splitext(os.path.basename(wkspath))[0]
1049 
1050         wicout = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1051 
1052         if not wicout:
1053             return (p, None)
1054 
1055         wicimg = wicout[0]
1056 
1057         if not native_sysroot:
1058             native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
1059 
1060         # verify partition size with wic
1061         res = runCmd("parted -m %s unit kib p" % wicimg,
1062                      native_sysroot=native_sysroot, stderr=subprocess.PIPE)
1063 
1064         # parse parted output which looks like this:
1065         # BYT;\n
1066         # /var/tmp/wic/build/tmpfwvjjkf_-201611101222-hda.direct:200MiB:file:512:512:msdos::;\n
1067         # 1:0.00MiB:200MiB:200MiB:ext4::;\n
1068         return (p, res.output.splitlines()[2:])
1069 
1070     def test_fixed_size(self):
1071         """
1072         Test creation of a simple image with partition size controlled through
1073         --fixed-size flag
1074         """
1075         wkspath = Wic2._make_fixed_size_wks(200)
1076         _, partlns = self._get_wic_partitions(wkspath)
1077         os.remove(wkspath)
1078 
1079         self.assertEqual(partlns, [
1080                         "1:4.00kiB:204804kiB:204800kiB:ext4::;",
1081                         ])
1082 
1083     def test_fixed_size_error(self):
1084         """
1085         Test creation of a simple image with partition size controlled through
1086         --fixed-size flag. The size of partition is intentionally set to 1MiB
1087         in order to trigger an error in wic.
1088         """
1089         wkspath = Wic2._make_fixed_size_wks(1)
1090         p, _ = self._get_wic_partitions(wkspath, ignore_status=True)
1091         os.remove(wkspath)
1092 
1093         self.assertNotEqual(p.status, 0, "wic exited successfully when an error was expected:\n%s" % p.output)
1094 
1095     def test_offset(self):
1096         native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
1097 
1098         with NamedTemporaryFile("w", suffix=".wks") as tempf:
1099             # Test that partitions are placed at the correct offsets, default KB
1100             tempf.write("bootloader --ptable gpt\n" \
1101                         "part /    --source rootfs --ondisk hda --offset 32     --fixed-size 100M --fstype=ext4\n" \
1102                         "part /bar                 --ondisk hda --offset 102432 --fixed-size 100M --fstype=ext4\n")
1103             tempf.flush()
1104 
1105             _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
1106             self.assertEqual(partlns, [
1107                 "1:32.0kiB:102432kiB:102400kiB:ext4:primary:;",
1108                 "2:102432kiB:204832kiB:102400kiB:ext4:primary:;",
1109                 ])
1110 
1111         with NamedTemporaryFile("w", suffix=".wks") as tempf:
1112             # Test that partitions are placed at the correct offsets, same with explicit KB
1113             tempf.write("bootloader --ptable gpt\n" \
1114                         "part /    --source rootfs --ondisk hda --offset 32K     --fixed-size 100M --fstype=ext4\n" \
1115                         "part /bar                 --ondisk hda --offset 102432K --fixed-size 100M --fstype=ext4\n")
1116             tempf.flush()
1117 
1118             _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
1119             self.assertEqual(partlns, [
1120                 "1:32.0kiB:102432kiB:102400kiB:ext4:primary:;",
1121                 "2:102432kiB:204832kiB:102400kiB:ext4:primary:;",
1122                 ])
1123 
1124         with NamedTemporaryFile("w", suffix=".wks") as tempf:
1125             # Test that partitions are placed at the correct offsets using MB
1126             tempf.write("bootloader --ptable gpt\n" \
1127                         "part /    --source rootfs --ondisk hda --offset 32K  --fixed-size 100M --fstype=ext4\n" \
1128                         "part /bar                 --ondisk hda --offset 101M --fixed-size 100M --fstype=ext4\n")
1129             tempf.flush()
1130 
1131             _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
1132             self.assertEqual(partlns, [
1133                 "1:32.0kiB:102432kiB:102400kiB:ext4:primary:;",
1134                 "2:103424kiB:205824kiB:102400kiB:ext4:primary:;",
1135                 ])
1136 
1137         with NamedTemporaryFile("w", suffix=".wks") as tempf:
1138             # Test that partitions can be placed on a 512 byte sector boundary
1139             tempf.write("bootloader --ptable gpt\n" \
1140                         "part /    --source rootfs --ondisk hda --offset 65s --fixed-size 99M --fstype=ext4\n" \
1141                         "part /bar                 --ondisk hda --offset 102432 --fixed-size 100M --fstype=ext4\n")
1142             tempf.flush()
1143 
1144             _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
1145             self.assertEqual(partlns, [
1146                 "1:32.5kiB:101408kiB:101376kiB:ext4:primary:;",
1147                 "2:102432kiB:204832kiB:102400kiB:ext4:primary:;",
1148                 ])
1149 
1150         with NamedTemporaryFile("w", suffix=".wks") as tempf:
1151             # Test that a partition can be placed immediately after a MSDOS partition table
1152             tempf.write("bootloader --ptable msdos\n" \
1153                         "part /    --source rootfs --ondisk hda --offset 1s --fixed-size 100M --fstype=ext4\n")
1154             tempf.flush()
1155 
1156             _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
1157             self.assertEqual(partlns, [
1158                 "1:0.50kiB:102400kiB:102400kiB:ext4::;",
1159                 ])
1160 
1161         with NamedTemporaryFile("w", suffix=".wks") as tempf:
1162             # Test that image creation fails if the partitions would overlap
1163             tempf.write("bootloader --ptable gpt\n" \
1164                         "part /    --source rootfs --ondisk hda --offset 32     --fixed-size 100M --fstype=ext4\n" \
1165                         "part /bar                 --ondisk hda --offset 102431 --fixed-size 100M --fstype=ext4\n")
1166             tempf.flush()
1167 
1168             p, _ = self._get_wic_partitions(tempf.name, ignore_status=True)
1169             self.assertNotEqual(p.status, 0, "wic exited successfully when an error was expected:\n%s" % p.output)
1170 
1171         with NamedTemporaryFile("w", suffix=".wks") as tempf:
1172             # Test that partitions are not allowed to overlap with the booloader
1173             tempf.write("bootloader --ptable gpt\n" \
1174                         "part /    --source rootfs --ondisk hda --offset 8 --fixed-size 100M --fstype=ext4\n")
1175             tempf.flush()
1176 
1177             p, _ = self._get_wic_partitions(tempf.name, ignore_status=True)
1178             self.assertNotEqual(p.status, 0, "wic exited successfully when an error was expected:\n%s" % p.output)
1179 
1180     def test_extra_space(self):
1181         native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
1182 
1183         with NamedTemporaryFile("w", suffix=".wks") as tempf:
1184             tempf.write("bootloader --ptable gpt\n" \
1185                         "part /     --source rootfs --ondisk hda --extra-space 200M --fstype=ext4\n")
1186             tempf.flush()
1187 
1188             _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
1189             self.assertEqual(len(partlns), 1)
1190             size = partlns[0].split(':')[3]
1191             self.assertRegex(size, r'^[0-9]+kiB$')
1192             size = int(size[:-3])
1193             self.assertGreaterEqual(size, 204800)
1194 
1195     # TODO this test could also work on aarch64
1196     @skipIfNotArch(['i586', 'i686', 'x86_64'])
1197     @OETestTag("runqemu")
1198     def test_rawcopy_plugin_qemu(self):
1199         """Test rawcopy plugin in qemu"""
1200         # build ext4 and then use it for a wic image
1201         config = 'IMAGE_FSTYPES = "ext4"\n'
1202         self.append_config(config)
1203         bitbake('core-image-minimal')
1204         image_link_name = get_bb_var('IMAGE_LINK_NAME', 'core-image-minimal')
1205         self.remove_config(config)
1206 
1207         config = 'IMAGE_FSTYPES = "wic"\n' \
1208                  'IMAGE_LINK_NAME_CORE_IMAGE_MINIMAL = "%s"\n'\
1209                  'WKS_FILE = "test_rawcopy_plugin.wks.in"\n'\
1210                  % image_link_name
1211         self.append_config(config)
1212         bitbake('core-image-minimal-mtdutils')
1213         self.remove_config(config)
1214 
1215         runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-minimal-mtdutils') or ""
1216         with runqemu('core-image-minimal-mtdutils', ssh=False,
1217                      runqemuparams='%s nographic' % (runqemu_params), image_fstype='wic') as qemu:
1218             cmd = "grep sda. /proc/partitions  |wc -l"
1219             status, output = qemu.run_serial(cmd)
1220             self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1221             self.assertEqual(output, '2')
1222 
1223     def _rawcopy_plugin(self, fstype):
1224         """Test rawcopy plugin"""
1225         image = 'core-image-minimal'
1226         bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image)
1227         params = ',unpack' if fstype.endswith('.gz') else ''
1228         with NamedTemporaryFile("w", suffix=".wks") as wks:
1229             wks.write('part / --source rawcopy --sourceparams="file=%s.%s%s"\n'\
1230                       % (bb_vars['IMAGE_LINK_NAME'], fstype, params))
1231             wks.flush()
1232             cmd = "wic create %s -e %s -o %s" % (wks.name, image, self.resultdir)
1233             runCmd(cmd)
1234             wksname = os.path.splitext(os.path.basename(wks.name))[0]
1235             out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1236             self.assertEqual(1, len(out))
1237 
1238     def test_rawcopy_plugin(self):
1239         config = 'IMAGE_FSTYPES = "ext4"\n'
1240         self.append_config(config)
1241         self.assertEqual(0, bitbake('core-image-minimal').status)
1242         self.remove_config(config)
1243         self._rawcopy_plugin('ext4')
1244 
1245     def test_rawcopy_plugin_unpack(self):
1246         fstype = 'ext4.gz'
1247         config = 'IMAGE_FSTYPES = "%s"\n' % fstype
1248         self.append_config(config)
1249         self.assertEqual(0, bitbake('core-image-minimal').status)
1250         self.remove_config(config)
1251         self._rawcopy_plugin(fstype)
1252 
1253     def test_empty_plugin(self):
1254         """Test empty plugin"""
1255         config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "test_empty_plugin.wks"\n'
1256         self.append_config(config)
1257         image = 'core-image-minimal'
1258         bitbake(image)
1259         self.remove_config(config)
1260         bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image)
1261         image_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], '%s.wic' % bb_vars['IMAGE_LINK_NAME'])
1262         self.assertTrue(os.path.exists(image_path), msg="Image file %s wasn't generated as expected" % image_path)
1263 
1264         sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1265 
1266         # Fstype column from 'wic ls' should be empty for the second partition
1267         # as listed in test_empty_plugin.wks
1268         result = runCmd("wic ls %s -n %s | awk -F ' ' '{print $1 \" \" $5}' | grep '^2' | wc -w" % (image_path, sysroot))
1269         self.assertEqual('1', result.output)
1270 
1271     @skipIfNotArch(['i586', 'i686', 'x86_64'])
1272     @OETestTag("runqemu")
1273     def test_biosplusefi_plugin_qemu(self):
1274         """Test biosplusefi plugin in qemu"""
1275         config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "test_biosplusefi_plugin.wks"\nMACHINE_FEATURES:append = " efi"\n'
1276         self.append_config(config)
1277         bitbake('core-image-minimal')
1278         self.remove_config(config)
1279 
1280         runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-minimal') or ""
1281         with runqemu('core-image-minimal', ssh=False,
1282                      runqemuparams='%s nographic' % (runqemu_params), image_fstype='wic') as qemu:
1283             # Check that we have ONLY two /dev/sda* partitions (/boot and /)
1284             cmd = "grep sda. /proc/partitions | wc -l"
1285             status, output = qemu.run_serial(cmd)
1286             self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1287             self.assertEqual(output, '2')
1288             # Check that /dev/sda1 is /boot and that either /dev/root OR /dev/sda2 is /
1289             cmd = "mount | grep '^/dev/' | cut -f1,3 -d ' ' | egrep -c -e '/dev/sda1 /boot' -e '/dev/root /|/dev/sda2 /'"
1290             status, output = qemu.run_serial(cmd)
1291             self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1292             self.assertEqual(output, '2')
1293             # Check that /boot has EFI bootx64.efi (required for EFI)
1294             cmd = "ls /boot/EFI/BOOT/bootx64.efi | wc -l"
1295             status, output = qemu.run_serial(cmd)
1296             self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1297             self.assertEqual(output, '1')
1298             # Check that "BOOTABLE" flag is set on boot partition (required for PC-Bios)
1299             # Trailing "cat" seems to be required; otherwise run_serial() sends back echo of the input command
1300             cmd = "fdisk -l /dev/sda | grep /dev/sda1 | awk {print'$2'} | cat"
1301             status, output = qemu.run_serial(cmd)
1302             self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1303             self.assertEqual(output, '*')
1304 
1305     @skipIfNotArch(['i586', 'i686', 'x86_64'])
1306     def test_biosplusefi_plugin(self):
1307         """Test biosplusefi plugin"""
1308         # Wic generation below may fail depending on the order of the unittests
1309         # This is because bootimg-pcbios (that bootimg-biosplusefi uses) generate its MBR inside STAGING_DATADIR directory
1310         #    which may or may not exists depending on what was built already
1311         # If an image hasn't been built yet, directory ${STAGING_DATADIR}/syslinux won't exists and _get_bootimg_dir()
1312         #   will raise with "Couldn't find correct bootimg_dir"
1313         # The easiest way to work-around this issue is to make sure we already built an image here, hence the bitbake call
1314         config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "test_biosplusefi_plugin.wks"\nMACHINE_FEATURES:append = " efi"\n'
1315         self.append_config(config)
1316         bitbake('core-image-minimal')
1317         self.remove_config(config)
1318 
1319         img = 'core-image-minimal'
1320         with NamedTemporaryFile("w", suffix=".wks") as wks:
1321             wks.writelines(['part /boot --active --source bootimg-biosplusefi --sourceparams="loader=grub-efi"\n',
1322                             'part / --source rootfs --fstype=ext4 --align 1024 --use-uuid\n'\
1323                             'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n'])
1324             wks.flush()
1325             cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1326             runCmd(cmd)
1327             wksname = os.path.splitext(os.path.basename(wks.name))[0]
1328             out = glob(os.path.join(self.resultdir, "%s-*.direct" % wksname))
1329             self.assertEqual(1, len(out))
1330 
1331     @skipIfNotArch(['i586', 'i686', 'x86_64', 'aarch64'])
1332     def test_uefi_kernel(self):
1333         """ Test uefi-kernel in wic """
1334         config = 'IMAGE_EFI_BOOT_FILES="/etc/fstab;testfile"\nIMAGE_FSTYPES = "wic"\nWKS_FILE = "test_uefikernel.wks"\nMACHINE_FEATURES:append = " efi"\n'
1335         self.append_config(config)
1336         bitbake('core-image-minimal')
1337         self.remove_config(config)
1338 
1339         img = 'core-image-minimal'
1340         with NamedTemporaryFile("w", suffix=".wks") as wks:
1341             wks.writelines(['part /boot --source bootimg-efi --sourceparams="loader=uefi-kernel"\n'
1342                             'part / --source rootfs --fstype=ext4 --align 1024 --use-uuid\n'\
1343                             'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n'])
1344             wks.flush()
1345             cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1346             runCmd(cmd)
1347             wksname = os.path.splitext(os.path.basename(wks.name))[0]
1348             out = glob(os.path.join(self.resultdir, "%s-*.direct" % wksname))
1349             self.assertEqual(1, len(out))
1350 
1351     # TODO this test could also work on aarch64
1352     @skipIfNotArch(['i586', 'i686', 'x86_64'])
1353     @OETestTag("runqemu")
1354     def test_efi_plugin_unified_kernel_image_qemu(self):
1355         """Test Unified Kernel Image feature in qemu without systemd in initramfs or rootfs"""
1356         config = """
1357 # efi firmware must load systemd-boot, not grub
1358 EFI_PROVIDER = "systemd-boot"
1359 
1360 # image format must be wic, needs esp partition for firmware etc
1361 IMAGE_FSTYPES:pn-core-image-base:append = " wic"
1362 WKS_FILE = "test_efi_plugin.wks"
1363 
1364 # efi, uki and systemd features must be enabled
1365 MACHINE_FEATURES:append = " efi"
1366 DISTRO_FEATURES_NATIVE:append = " systemd"
1367 IMAGE_CLASSES:append:pn-core-image-base = " uki"
1368 
1369 # uki embeds also an initrd, no systemd or udev
1370 INITRAMFS_IMAGE = "core-image-initramfs-boot"
1371 
1372 # runqemu must not load kernel separately, it's in the uki
1373 QB_KERNEL_ROOT = ""
1374 QB_DEFAULT_KERNEL = "none"
1375 
1376 # boot command line provided via uki, not via bootloader
1377 UKI_CMDLINE = "rootwait root=LABEL=root console=${KERNEL_CONSOLE}"
1378 
1379 """
1380         self.append_config(config)
1381         bitbake('core-image-base ovmf')
1382         runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-base') or ""
1383         uki_filename = get_bb_var('UKI_FILENAME', 'core-image-base')
1384         self.remove_config(config)
1385 
1386         with runqemu('core-image-base', ssh=False,
1387                      runqemuparams='%s nographic ovmf' % (runqemu_params), image_fstype='wic') as qemu:
1388             # Check that /boot has EFI boot*.efi (required for EFI)
1389             cmd = "ls /boot/EFI/BOOT/boot*.efi | wc -l"
1390             status, output = qemu.run_serial(cmd)
1391             self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1392             self.assertEqual(output, '1')
1393             # Check that /boot has EFI/Linux/${UKI_FILENAME} (required for Unified Kernel Images auto detection)
1394             cmd = "ls /boot/EFI/Linux/%s | wc -l" % (uki_filename)
1395             status, output = qemu.run_serial(cmd)
1396             self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1397             self.assertEqual(output, '1')
1398             # Check that /boot doesn't have loader/entries/boot.conf (Unified Kernel Images are auto detected by the bootloader)
1399             cmd = "ls /boot/loader/entries/boot.conf 2&>/dev/null | wc -l"
1400             status, output = qemu.run_serial(cmd)
1401             self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1402             self.assertEqual(output, '0')
1403 
1404     @skipIfNotArch(['aarch64'])
1405     @OETestTag("runqemu")
1406     def test_efi_plugin_plain_systemd_boot_qemu_aarch64(self):
1407         """Test plain systemd-boot in qemu with systemd"""
1408         config = """
1409 INIT_MANAGER = "systemd"
1410 EFI_PROVIDER = "systemd-boot"
1411 
1412 # image format must be wic, needs esp partition for firmware etc
1413 IMAGE_FSTYPES:pn-core-image-base:append = " wic"
1414 WKS_FILE = "test_efi_plugin_plain_systemd-boot.wks"
1415 
1416 INITRAMFS_IMAGE = "core-image-initramfs-boot"
1417 
1418 # to configure runqemu
1419 IMAGE_CLASSES += "qemuboot"
1420 # u-boot efi firmware
1421 QB_DEFAULT_BIOS = "u-boot.bin"
1422 # need to use virtio, scsi not supported by u-boot by default
1423 QB_DRIVE_TYPE = "/dev/vd"
1424 
1425 # disable kvm, breaks boot
1426 QEMU_USE_KVM = ""
1427 
1428 IMAGE_CLASSES:remove = 'testimage'
1429 """
1430         self.append_config(config)
1431         bitbake('core-image-base u-boot')
1432         runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-base') or ""
1433 
1434         with runqemu('core-image-base', ssh=False,
1435                      runqemuparams='%s nographic' % (runqemu_params), image_fstype='wic') as qemu:
1436             # Check that /boot has EFI boot*.efi (required for EFI)
1437             cmd = "ls /boot/EFI/BOOT/boot*.efi | wc -l"
1438             status, output = qemu.run_serial(cmd)
1439             self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1440             self.assertEqual(output, '1')
1441             # Check that boot.conf exists
1442             cmd = "cat /boot/loader/entries/boot.conf"
1443             status, output = qemu.run_serial(cmd)
1444             self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1445         self.remove_config(config)
1446 
1447     @skipIfNotArch(['i586', 'i686', 'x86_64'])
1448     @OETestTag("runqemu")
1449     def test_efi_plugin_plain_systemd_boot_qemu_x86(self):
1450         """Test plain systemd-boot to systemd in qemu"""
1451         config = """
1452 INIT_MANAGER = "systemd"
1453 EFI_PROVIDER = "systemd-boot"
1454 
1455 # image format must be wic, needs esp partition for firmware etc
1456 IMAGE_FSTYPES:pn-core-image-base:append = " wic"
1457 WKS_FILE = "test_efi_plugin_plain_systemd-boot.wks"
1458 
1459 INITRAMFS_IMAGE = "core-image-initramfs-boot"
1460 """
1461         self.append_config(config)
1462         bitbake('core-image-base ovmf')
1463         runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-base') or ""
1464         self.remove_config(config)
1465 
1466         with runqemu('core-image-base', ssh=False,
1467                      runqemuparams='%s nographic ovmf' % (runqemu_params), image_fstype='wic') as qemu:
1468             # Check that /boot has EFI boot*.efi (required for EFI)
1469             cmd = "ls /boot/EFI/BOOT/boot*.efi | wc -l"
1470             status, output = qemu.run_serial(cmd)
1471             self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1472             self.assertEqual(output, '1')
1473             # Check that boot.conf exists
1474             cmd = "cat /boot/loader/entries/boot.conf"
1475             status, output = qemu.run_serial(cmd)
1476             self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1477 
1478     def test_fs_types(self):
1479         """Test filesystem types for empty and not empty partitions"""
1480         img = 'core-image-minimal'
1481         with NamedTemporaryFile("w", suffix=".wks") as wks:
1482             wks.writelines(['part ext2   --fstype ext2     --source rootfs\n',
1483                             'part btrfs  --fstype btrfs    --source rootfs --size 40M\n',
1484                             'part squash --fstype squashfs --source rootfs\n',
1485                             'part swap   --fstype swap --size 1M\n',
1486                             'part emptyvfat   --fstype vfat   --size 1M\n',
1487                             'part emptymsdos  --fstype msdos  --size 1M\n',
1488                             'part emptyext2   --fstype ext2   --size 1M\n',
1489                             'part emptybtrfs  --fstype btrfs  --size 150M\n'])
1490             wks.flush()
1491             cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1492             runCmd(cmd)
1493             wksname = os.path.splitext(os.path.basename(wks.name))[0]
1494             out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1495             self.assertEqual(1, len(out))
1496 
1497     def test_kickstart_parser(self):
1498         """Test wks parser options"""
1499         with NamedTemporaryFile("w", suffix=".wks") as wks:
1500             wks.writelines(['part / --fstype ext3 --source rootfs --system-id 0xFF '\
1501                             '--overhead-factor 1.2 --size 100k\n'])
1502             wks.flush()
1503             cmd = "wic create %s -e core-image-minimal -o %s" % (wks.name, self.resultdir)
1504             runCmd(cmd)
1505             wksname = os.path.splitext(os.path.basename(wks.name))[0]
1506             out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1507             self.assertEqual(1, len(out))
1508 
1509     def test_image_bootpart_globbed(self):
1510         """Test globbed sources with image-bootpart plugin"""
1511         img = "core-image-minimal"
1512         cmd = "wic create sdimage-bootpart -e %s -o %s" % (img, self.resultdir)
1513         config = 'IMAGE_BOOT_FILES = "%s*"' % get_bb_var('KERNEL_IMAGETYPE', img)
1514         self.append_config(config)
1515         runCmd(cmd)
1516         self.remove_config(config)
1517         self.assertEqual(1, len(glob(os.path.join(self.resultdir, "sdimage-bootpart-*direct"))))
1518 
1519     def test_sparse_copy(self):
1520         """Test sparse_copy with FIEMAP and SEEK_HOLE filemap APIs"""
1521         libpath = os.path.join(self.td['COREBASE'], 'scripts', 'lib', 'wic')
1522         sys.path.insert(0, libpath)
1523         from  filemap import FilemapFiemap, FilemapSeek, sparse_copy, ErrorNotSupp
1524         with NamedTemporaryFile("w", suffix=".wic-sparse") as sparse:
1525             src_name = sparse.name
1526             src_size = 1024 * 10
1527             sparse.truncate(src_size)
1528             # write one byte to the file
1529             with open(src_name, 'r+b') as sfile:
1530                 sfile.seek(1024 * 4)
1531                 sfile.write(b'\x00')
1532             dest = sparse.name + '.out'
1533             # copy src file to dest using different filemap APIs
1534             for api in (FilemapFiemap, FilemapSeek, None):
1535                 if os.path.exists(dest):
1536                     os.unlink(dest)
1537                 try:
1538                     sparse_copy(sparse.name, dest, api=api)
1539                 except ErrorNotSupp:
1540                     continue # skip unsupported API
1541                 dest_stat = os.stat(dest)
1542                 self.assertEqual(dest_stat.st_size, src_size)
1543                 # 8 blocks is 4K (physical sector size)
1544                 self.assertEqual(dest_stat.st_blocks, 8)
1545             os.unlink(dest)
1546 
1547     def test_mkfs_extraopts(self):
1548         """Test wks option --mkfs-extraopts for empty and not empty partitions"""
1549         img = 'core-image-minimal'
1550         with NamedTemporaryFile("w", suffix=".wks") as wks:
1551             wks.writelines(
1552                 ['part ext2   --fstype ext2     --source rootfs --mkfs-extraopts "-D -F -i 8192"\n',
1553                  "part btrfs  --fstype btrfs    --source rootfs --size 40M --mkfs-extraopts='--quiet'\n",
1554                  'part squash --fstype squashfs --source rootfs --mkfs-extraopts "-no-sparse -b 4096"\n',
1555                  'part emptyvfat   --fstype vfat   --size 1M --mkfs-extraopts "-S 1024 -s 64"\n',
1556                  'part emptymsdos  --fstype msdos  --size 1M --mkfs-extraopts "-S 1024 -s 64"\n',
1557                  'part emptyext2   --fstype ext2   --size 1M --mkfs-extraopts "-D -F -i 8192"\n',
1558                  'part emptybtrfs  --fstype btrfs  --size 100M --mkfs-extraopts "--mixed -K"\n'])
1559             wks.flush()
1560             cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1561             runCmd(cmd)
1562             wksname = os.path.splitext(os.path.basename(wks.name))[0]
1563             out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1564             self.assertEqual(1, len(out))
1565 
1566     @skipIfNotArch(['i586', 'i686', 'x86_64'])
1567     @OETestTag("runqemu")
1568     def test_expand_mbr_image(self):
1569         """Test wic write --expand command for mbr image"""
1570         # build an image
1571         config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "directdisk.wks"\n'
1572         self.append_config(config)
1573         image = 'core-image-minimal'
1574         bitbake(image)
1575 
1576         # get path to the image
1577         bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image)
1578         image_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], '%s.wic' % bb_vars['IMAGE_LINK_NAME'])
1579 
1580         self.remove_config(config)
1581 
1582         try:
1583             # expand image to 1G
1584             new_image_path = None
1585             with NamedTemporaryFile(mode='wb', suffix='.wic.exp',
1586                                     dir=bb_vars['DEPLOY_DIR_IMAGE'], delete=False) as sparse:
1587                 sparse.truncate(1024 ** 3)
1588                 new_image_path = sparse.name
1589 
1590             sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1591             cmd = "wic write -n %s --expand 1:0 %s %s" % (sysroot, image_path, new_image_path)
1592             runCmd(cmd)
1593 
1594             # check if partitions are expanded
1595             orig = runCmd("wic ls %s -n %s" % (image_path, sysroot))
1596             exp = runCmd("wic ls %s -n %s" % (new_image_path, sysroot))
1597             orig_sizes = [int(line.split()[3]) for line in orig.output.split('\n')[1:]]
1598             exp_sizes = [int(line.split()[3]) for line in exp.output.split('\n')[1:]]
1599             self.assertEqual(orig_sizes[0], exp_sizes[0]) # first partition is not resized
1600             self.assertTrue(orig_sizes[1] < exp_sizes[1], msg="Parition size wasn't enlarged (%s vs %s)" % (orig_sizes[1], exp_sizes[1]))
1601 
1602             # Check if all free space is partitioned
1603             result = runCmd("%s/usr/sbin/sfdisk -F %s" % (sysroot, new_image_path))
1604             self.assertIn("0 B, 0 bytes, 0 sectors", result.output)
1605 
1606             os.rename(image_path, image_path + '.bak')
1607             os.rename(new_image_path, image_path)
1608 
1609             runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-minimal') or ""
1610             with runqemu('core-image-minimal', ssh=False, runqemuparams='%s nographic' % (runqemu_params)) as qemu:
1611                 cmd = "ls /etc/"
1612                 status, output = qemu.run_serial('true')
1613                 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1614         finally:
1615             if os.path.exists(new_image_path):
1616                 os.unlink(new_image_path)
1617             if os.path.exists(image_path + '.bak'):
1618                 os.rename(image_path + '.bak', image_path)
1619 
1620     def test_gpt_partition_name(self):
1621         """Test --part-name argument to set partition name in GPT table"""
1622         config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "test_gpt_partition_name.wks"\n'
1623         self.append_config(config)
1624         image = 'core-image-minimal'
1625         bitbake(image)
1626         self.remove_config(config)
1627         deploy_dir = get_bb_var('DEPLOY_DIR_IMAGE')
1628         bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image)
1629         image_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], '%s.wic' % bb_vars['IMAGE_LINK_NAME'])
1630 
1631         sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1632 
1633         # Image is created
1634         self.assertTrue(os.path.exists(image_path), "image file %s doesn't exist" % image_path)
1635 
1636         # Check the names of the three partitions
1637         # as listed in test_gpt_partition_name.wks
1638         result = runCmd("%s/usr/sbin/sfdisk --part-label %s 1" % (sysroot, image_path))
1639         self.assertEqual('boot-A', result.output)
1640         result = runCmd("%s/usr/sbin/sfdisk --part-label %s 2" % (sysroot, image_path))
1641         self.assertEqual('root-A', result.output)
1642         # When the --part-name is not defined, the partition name is equal to the --label
1643         result = runCmd("%s/usr/sbin/sfdisk --part-label %s 3" % (sysroot, image_path))
1644         self.assertEqual('ext-space', result.output)
1645 
1646     def test_empty_zeroize_plugin(self):
1647         img = 'core-image-minimal'
1648         expected_size = [ 1024*1024,    # 1M
1649                           512*1024,     # 512K
1650                           2*1024*1024]  # 2M
1651         # Check combination of sourceparams
1652         with NamedTemporaryFile("w", suffix=".wks") as wks:
1653             wks.writelines(
1654                 ['part empty --source empty --sourceparams="fill" --ondisk sda --fixed-size 1M\n',
1655                  'part empty --source empty --sourceparams="size=512K" --ondisk sda --size 1M --align 1024\n',
1656                  'part empty --source empty --sourceparams="size=2048k,bs=512K" --ondisk sda --size 4M --align 1024\n'
1657                  ])
1658             wks.flush()
1659             cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1660             runCmd(cmd)
1661             wksname = os.path.splitext(os.path.basename(wks.name))[0]
1662             wicout = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1663             # Skip the complete image and just look at the single partitions
1664             for idx, value in enumerate(wicout[1:]):
1665                 self.logger.info(wicout[idx])
1666                 # Check if partitions are actually zeroized
1667                 with open(wicout[idx], mode="rb") as fd:
1668                     ba = bytearray(fd.read())
1669                     for b in ba:
1670                         self.assertEqual(b, 0)
1671                 self.assertEqual(expected_size[idx], os.path.getsize(wicout[idx]))
1672 
1673         # Check inconsistancy check between "fill" and "--size" parameter
1674         with NamedTemporaryFile("w", suffix=".wks") as wks:
1675             wks.writelines(['part empty --source empty --sourceparams="fill" --ondisk sda --size 1M\n'])
1676             wks.flush()
1677             cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1678             result = runCmd(cmd, ignore_status=True)
1679             self.assertIn("Source parameter 'fill' only works with the '--fixed-size' option, exiting.", result.output)
1680             self.assertNotEqual(0, result.status)
1681 
1682 class ModifyTests(WicTestCase):
1683     def test_wic_ls(self):
1684         """Test listing image content using 'wic ls'"""
1685         runCmd("wic create wictestdisk "
1686                                    "--image-name=core-image-minimal "
1687                                    "-D -o %s" % self.resultdir)
1688         images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
1689         self.assertEqual(1, len(images))
1690 
1691         sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1692 
1693         # list partitions
1694         result = runCmd("wic ls %s -n %s" % (images[0], sysroot))
1695         self.assertEqual(3, len(result.output.split('\n')))
1696 
1697         # list directory content of the first partition
1698         result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
1699         self.assertEqual(6, len(result.output.split('\n')))
1700 
1701     def test_wic_cp(self):
1702         """Test copy files and directories to the the wic image."""
1703         runCmd("wic create wictestdisk "
1704                                    "--image-name=core-image-minimal "
1705                                    "-D -o %s" % self.resultdir)
1706         images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
1707         self.assertEqual(1, len(images))
1708 
1709         sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1710 
1711         # list directory content of the first partition
1712         result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
1713         self.assertEqual(6, len(result.output.split('\n')))
1714 
1715         with NamedTemporaryFile("w", suffix=".wic-cp") as testfile:
1716             testfile.write("test")
1717 
1718             # copy file to the partition
1719             runCmd("wic cp %s %s:1/ -n %s" % (testfile.name, images[0], sysroot))
1720 
1721             # check if file is there
1722             result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
1723             self.assertEqual(7, len(result.output.split('\n')))
1724             self.assertIn(os.path.basename(testfile.name), result.output)
1725 
1726             # prepare directory
1727             testdir = os.path.join(self.resultdir, 'wic-test-cp-dir')
1728             testsubdir = os.path.join(testdir, 'subdir')
1729             os.makedirs(os.path.join(testsubdir))
1730             copy(testfile.name, testdir)
1731 
1732             # copy directory to the partition
1733             runCmd("wic cp %s %s:1/ -n %s" % (testdir, images[0], sysroot))
1734 
1735             # check if directory is there
1736             result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
1737             self.assertEqual(8, len(result.output.split('\n')))
1738             self.assertIn(os.path.basename(testdir), result.output)
1739 
1740             # copy the file from the partition and check if it success
1741             dest = '%s-cp' % testfile.name
1742             runCmd("wic cp %s:1/%s %s -n %s" % (images[0],
1743                     os.path.basename(testfile.name), dest, sysroot))
1744             self.assertTrue(os.path.exists(dest), msg="File %s wasn't generated as expected" % dest)
1745 
1746 
1747     def test_wic_rm(self):
1748         """Test removing files and directories from the the wic image."""
1749         runCmd("wic create mkefidisk "
1750                                    "--image-name=core-image-minimal "
1751                                    "-D -o %s" % self.resultdir)
1752         images = glob(os.path.join(self.resultdir, "mkefidisk-*.direct"))
1753         self.assertEqual(1, len(images))
1754 
1755         sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1756         # Not bulletproof but hopefully sufficient
1757         kerneltype = get_bb_var('KERNEL_IMAGETYPE', 'virtual/kernel')
1758 
1759         # list directory content of the first partition
1760         result = runCmd("wic ls %s:1 -n %s" % (images[0], sysroot))
1761         self.assertIn('\n%s ' % kerneltype.upper(), result.output)
1762         self.assertIn('\nEFI          <DIR>     ', result.output)
1763 
1764         # remove file. EFI partitions are case-insensitive so exercise that too
1765         runCmd("wic rm %s:1/%s -n %s" % (images[0], kerneltype.lower(), sysroot))
1766 
1767         # remove directory
1768         runCmd("wic rm %s:1/efi -n %s" % (images[0], sysroot))
1769 
1770         # check if they're removed
1771         result = runCmd("wic ls %s:1 -n %s" % (images[0], sysroot))
1772         self.assertNotIn('\n%s        ' % kerneltype.upper(), result.output)
1773         self.assertNotIn('\nEFI          <DIR>     ', result.output)
1774 
1775     def test_wic_ls_ext(self):
1776         """Test listing content of the ext partition using 'wic ls'"""
1777         runCmd("wic create wictestdisk "
1778                                    "--image-name=core-image-minimal "
1779                                    "-D -o %s" % self.resultdir)
1780         images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
1781         self.assertEqual(1, len(images))
1782 
1783         sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1784 
1785         # list directory content of the second ext4 partition
1786         result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot))
1787         self.assertTrue(set(['bin', 'home', 'proc', 'usr', 'var', 'dev', 'lib', 'sbin']).issubset(
1788                             set(line.split()[-1] for line in result.output.split('\n') if line)), msg="Expected directories not present %s" % result.output)
1789 
1790     def test_wic_cp_ext(self):
1791         """Test copy files and directories to the ext partition."""
1792         runCmd("wic create wictestdisk "
1793                                    "--image-name=core-image-minimal "
1794                                    "-D -o %s" % self.resultdir)
1795         images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
1796         self.assertEqual(1, len(images))
1797 
1798         sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1799 
1800         # list directory content of the ext4 partition
1801         result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot))
1802         dirs = set(line.split()[-1] for line in result.output.split('\n') if line)
1803         self.assertTrue(set(['bin', 'home', 'proc', 'usr', 'var', 'dev', 'lib', 'sbin']).issubset(dirs), msg="Expected directories not present %s" % dirs)
1804 
1805         with NamedTemporaryFile("w", suffix=".wic-cp") as testfile:
1806             testfile.write("test")
1807 
1808             # copy file to the partition
1809             runCmd("wic cp %s %s:2/ -n %s" % (testfile.name, images[0], sysroot))
1810 
1811             # check if file is there
1812             result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot))
1813             newdirs = set(line.split()[-1] for line in result.output.split('\n') if line)
1814             self.assertEqual(newdirs.difference(dirs), set([os.path.basename(testfile.name)]))
1815 
1816             # check if the file to copy is in the partition
1817             result = runCmd("wic ls %s:2/etc/ -n %s" % (images[0], sysroot))
1818             self.assertIn('fstab', [line.split()[-1] for line in result.output.split('\n') if line])
1819 
1820             # copy file from the partition, replace the temporary file content with it and
1821             # check for the file size to validate the copy
1822             runCmd("wic cp %s:2/etc/fstab %s -n %s" % (images[0], testfile.name, sysroot))
1823             self.assertTrue(os.stat(testfile.name).st_size > 0, msg="Filesize not as expected %s" % os.stat(testfile.name).st_size)
1824 
1825 
1826     def test_wic_rm_ext(self):
1827         """Test removing files from the ext partition."""
1828         runCmd("wic create mkefidisk "
1829                                    "--image-name=core-image-minimal "
1830                                    "-D -o %s" % self.resultdir)
1831         images = glob(os.path.join(self.resultdir, "mkefidisk-*.direct"))
1832         self.assertEqual(1, len(images))
1833 
1834         sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1835 
1836         # list directory content of the /etc directory on ext4 partition
1837         result = runCmd("wic ls %s:2/etc/ -n %s" % (images[0], sysroot))
1838         self.assertIn('fstab', [line.split()[-1] for line in result.output.split('\n') if line])
1839 
1840         # remove file
1841         runCmd("wic rm %s:2/etc/fstab -n %s" % (images[0], sysroot))
1842 
1843         # check if it's removed
1844         result = runCmd("wic ls %s:2/etc/ -n %s" % (images[0], sysroot))
1845         self.assertNotIn('fstab', [line.split()[-1] for line in result.output.split('\n') if line])
1846 
1847         # remove non-empty directory
1848         runCmd("wic rm -r %s:2/etc/ -n %s" % (images[0], sysroot))
1849 
1850         # check if it's removed
1851         result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot))
1852         self.assertNotIn('etc', [line.split()[-1] for line in result.output.split('\n') if line])
1853