2 # Copyright (c) 2015, Intel Corporation.
4 # SPDX-License-Identifier: GPL-2.0-only
7 # Ed Bartosh <ed.bartosh@linux.intel.com>
9 """Test cases for wic."""
18 from shutil import rmtree, copy
19 from tempfile import NamedTemporaryFile
20 from tempfile import TemporaryDirectory
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
28 def extract_files(debugfs_output):
30 extract file names from the output of debugfs -R 'ls -p',
31 which looks like this:
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
42 # NOTE the occasional ^M in file names
43 return [line.split('/')[5].strip() for line in \
44 debugfs_output.strip().split('/\n')]
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']:
53 class WicTestCase(OESelftestTestCase):
56 image_is_ready = False
60 """This code is executed before each test method."""
61 self.resultdir = os.path.join(self.builddir, "wic-tmp")
62 super(WicTestCase, self).setUpLocal()
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')
71 bitbake('wic-tools core-image-minimal core-image-minimal-mtdutils')
72 WicTestCase.image_is_ready = True
73 rmtree(self.resultdir, ignore_errors=True)
75 def tearDownLocal(self):
76 """Remove resultdir as it may contain images."""
77 rmtree(self.resultdir, ignore_errors=True)
78 super(WicTestCase, self).tearDownLocal()
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]
89 class CLITests(OESelftestTestCase):
90 def test_version(self):
91 """Test wic --version"""
92 runCmd('wic --version')
95 """Test wic --help and wic -h"""
99 def test_createhelp(self):
100 """Test wic create --help"""
101 runCmd('wic create --help')
103 def test_listhelp(self):
104 """Test wic list --help"""
105 runCmd('wic list --help')
107 def test_help_create(self):
108 """Test wic help create"""
109 runCmd('wic help create')
111 def test_help_list(self):
112 """Test wic help list"""
113 runCmd('wic help list')
115 def test_help_overview(self):
116 """Test wic help overview"""
117 runCmd('wic help overview')
119 def test_help_plugins(self):
120 """Test wic help plugins"""
121 runCmd('wic help plugins')
123 def test_help_kickstart(self):
124 """Test wic help kickstart"""
125 runCmd('wic help kickstart')
127 def test_list_images(self):
128 """Test wic list images"""
129 runCmd('wic list images')
131 def test_list_source_plugins(self):
132 """Test wic list source-plugins"""
133 runCmd('wic list source-plugins')
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)
142 def test_unsupported_subcommand(self):
143 """Test unsupported subcommand"""
144 self.assertNotEqual(0, runCmd('wic unsupported', ignore_status=True).status)
146 def test_no_command(self):
147 """Test wic without command"""
148 self.assertEqual(1, runCmd('wic', ignore_status=True).status)
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:
156 'part --source bootimg_efi '
157 '--sourceparams="loader=grub-efi,install-kernel-into-boot-dir=false" '
158 '--label boot --active\n'
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)
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)
176 # check if the kernel is installed or not
177 kimgtype = get_bb_var('KERNEL_IMAGETYPE', img)
178 for file in os.listdir(tmpdir):
180 raise AssertionError(
181 "The kernel image '{}' was found in the partition".format(kimgtype)
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:
189 'part --source bootimg_efi '
190 '--sourceparams="loader=grub-efi,install-kernel-into-boot-dir=true" '
191 '--label boot --active\n'
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)
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)
207 # check if the kernel is installed or not
208 kimgtype = get_bb_var('KERNEL_IMAGETYPE', img)
210 for file in os.listdir(tmpdir):
215 found, "The kernel image '{}' was not found in the boot partition".format(kimgtype)
218 @skipIfNotArch(['x86_64'])
219 def test_grub_install_pcbios(self):
221 Test the installation of the grub modules + config
222 into the boot directory in the resulting wic image.
225 # create a temporary file for the WKS content
226 with NamedTemporaryFile("w", suffix=".wks") as wks:
228 'part --source bootimg_pcbios --sourceparams="loader-bios=grub" '
229 '--offset 1024 --fixed-size 78M --label boot --active\n'
230 'bootloader --ptable msdos --source bootimg_pcbios\n'
233 # create a temporary directory to extract the disk image to
234 with TemporaryDirectory() as tmpdir:
235 img = "core-image-minimal"
236 config = 'DEPENDS:pn-%s += "grub-native grub"' % (img)
238 self.append_config(config)
240 self.remove_config(config)
242 cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
245 wksname = os.path.splitext(os.path.basename(wks.name))[0]
246 out = glob(os.path.join(self.resultdir, "%s-*.direct" % wksname))
247 self.assertEqual(1, len(out))
249 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
251 # Check if grub.cfg is installed
252 result = runCmd("wic ls %s:1/boot/grub -n %s" % (out[0], sysroot))
253 self.assertIn('grub', result.output)
255 # Check if normal.mod is installed
256 result = runCmd("wic ls %s:1/boot/grub/i386-pc -n %s" % (out[0], sysroot))
257 self.assertIn('normal', result.output)
259 def test_build_image_name(self):
260 """Test wic create wictestdisk --image-name=core-image-minimal"""
261 cmd = "wic create wictestdisk --image-name=core-image-minimal -o %s" % self.resultdir
263 self.assertEqual(1, len(glob(os.path.join (self.resultdir, "wictestdisk-*.direct"))))
265 @skipIfNotArch(['i586', 'i686', 'x86_64'])
266 def test_gpt_image(self):
267 """Test creation of core-image-minimal with gpt table and UUID boot"""
268 cmd = "wic create directdisk-gpt --image-name core-image-minimal -o %s" % self.resultdir
270 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "directdisk-*.direct"))))
272 @skipIfNotArch(['i586', 'i686', 'x86_64'])
273 def test_iso_image(self):
274 """Test creation of hybrid iso image with legacy and EFI boot"""
275 config = 'INITRAMFS_IMAGE = "core-image-minimal-initramfs"\n'\
276 'MACHINE_FEATURES:append = " efi"\n'\
277 'DEPENDS:pn-core-image-minimal += "syslinux"\n'
278 self.append_config(config)
279 bitbake('core-image-minimal core-image-minimal-initramfs')
280 self.remove_config(config)
281 cmd = "wic create mkhybridiso --image-name core-image-minimal -o %s" % self.resultdir
283 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "HYBRID_ISO_IMG-*.direct"))))
284 self.assertEqual(1, len(glob(os.path.join (self.resultdir, "HYBRID_ISO_IMG-*.iso"))))
286 @skipIfNotArch(['i586', 'i686', 'x86_64'])
287 def test_qemux86_directdisk(self):
288 """Test creation of qemux-86-directdisk image"""
289 cmd = "wic create qemux86-directdisk -e core-image-minimal -o %s" % self.resultdir
291 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "qemux86-directdisk-*direct"))))
293 @skipIfNotArch(['i586', 'i686', 'x86_64', 'aarch64'])
294 def test_mkefidisk(self):
295 """Test creation of mkefidisk image"""
296 cmd = "wic create mkefidisk -e core-image-minimal -o %s" % self.resultdir
298 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "mkefidisk-*direct"))))
300 @skipIfNotArch(['i586', 'i686', 'x86_64'])
301 def test_bootloader_config(self):
302 """Test creation of directdisk-bootloader-config image"""
303 config = 'DEPENDS:pn-core-image-minimal += "syslinux"\n'
304 self.append_config(config)
305 bitbake('core-image-minimal')
306 self.remove_config(config)
307 cmd = "wic create directdisk-bootloader-config -e core-image-minimal -o %s" % self.resultdir
309 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "directdisk-bootloader-config-*direct"))))
311 @skipIfNotArch(['i586', 'i686', 'x86_64', 'aarch64'])
312 def test_systemd_bootdisk(self):
313 """Test creation of systemd-bootdisk image"""
314 config = 'MACHINE_FEATURES:append = " efi"\n'
315 self.append_config(config)
316 bitbake('core-image-minimal')
317 self.remove_config(config)
318 cmd = "wic create systemd-bootdisk -e core-image-minimal -o %s" % self.resultdir
320 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "systemd-bootdisk-*direct"))))
322 def test_efi_bootpart(self):
323 """Test creation of efi-bootpart image"""
324 cmd = "wic create mkefidisk -e core-image-minimal -o %s" % self.resultdir
325 kimgtype = get_bb_var('KERNEL_IMAGETYPE', 'core-image-minimal')
326 self.append_config('IMAGE_EFI_BOOT_FILES = "%s;kernel"\n' % kimgtype)
328 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
329 images = glob(os.path.join(self.resultdir, "mkefidisk-*.direct"))
330 result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
331 self.assertIn("kernel",result.output)
333 def test_sdimage_bootpart(self):
334 """Test creation of sdimage-bootpart image"""
335 cmd = "wic create sdimage-bootpart -e core-image-minimal -o %s" % self.resultdir
336 kimgtype = get_bb_var('KERNEL_IMAGETYPE', 'core-image-minimal')
337 self.write_config('IMAGE_BOOT_FILES = "%s"\n' % kimgtype)
339 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "sdimage-bootpart-*direct"))))
341 # TODO this doesn't have to be x86-specific
342 @skipIfNotArch(['i586', 'i686', 'x86_64'])
343 def test_default_output_dir(self):
344 """Test default output location"""
345 for fname in glob("directdisk-*.direct"):
347 config = 'DEPENDS:pn-core-image-minimal += "syslinux"\n'
348 self.append_config(config)
349 bitbake('core-image-minimal')
350 self.remove_config(config)
351 cmd = "wic create directdisk -e core-image-minimal"
353 self.assertEqual(1, len(glob("directdisk-*.direct")))
355 @skipIfNotArch(['i586', 'i686', 'x86_64'])
356 def test_build_artifacts(self):
357 """Test wic create directdisk providing all artifacts."""
358 bb_vars = get_bb_vars(['STAGING_DATADIR', 'RECIPE_SYSROOT_NATIVE'],
360 bb_vars.update(get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_ROOTFS'],
361 'core-image-minimal'))
362 bbvars = {key.lower(): value for key, value in bb_vars.items()}
363 bbvars['resultdir'] = self.resultdir
364 runCmd("wic create directdisk "
365 "-b %(staging_datadir)s "
366 "-k %(deploy_dir_image)s "
367 "-n %(recipe_sysroot_native)s "
368 "-r %(image_rootfs)s "
369 "-o %(resultdir)s" % bbvars)
370 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "directdisk-*.direct"))))
372 def test_compress_gzip(self):
373 """Test compressing an image with gzip"""
374 runCmd("wic create wictestdisk "
375 "--image-name core-image-minimal "
376 "-c gzip -o %s" % self.resultdir)
377 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct.gz"))))
379 def test_compress_bzip2(self):
380 """Test compressing an image with bzip2"""
381 runCmd("wic create wictestdisk "
382 "--image-name=core-image-minimal "
383 "-c bzip2 -o %s" % self.resultdir)
384 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct.bz2"))))
386 def test_compress_xz(self):
387 """Test compressing an image with xz"""
388 runCmd("wic create wictestdisk "
389 "--image-name=core-image-minimal "
390 "--compress-with=xz -o %s" % self.resultdir)
391 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct.xz"))))
393 def test_wrong_compressor(self):
394 """Test how wic breaks if wrong compressor is provided"""
395 self.assertEqual(2, runCmd("wic create wictestdisk "
396 "--image-name=core-image-minimal "
397 "-c wrong -o %s" % self.resultdir,
398 ignore_status=True).status)
400 def test_debug_short(self):
402 runCmd("wic create wictestdisk "
403 "--image-name=core-image-minimal "
404 "-D -o %s" % self.resultdir)
405 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
406 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "tmp.wic*"))))
408 def test_debug_long(self):
409 """Test --debug option"""
410 runCmd("wic create wictestdisk "
411 "--image-name=core-image-minimal "
412 "--debug -o %s" % self.resultdir)
413 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
414 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "tmp.wic*"))))
416 def test_skip_build_check_short(self):
418 runCmd("wic create wictestdisk "
419 "--image-name=core-image-minimal "
420 "-s -o %s" % self.resultdir)
421 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
423 def test_skip_build_check_long(self):
424 """Test --skip-build-check option"""
425 runCmd("wic create wictestdisk "
426 "--image-name=core-image-minimal "
427 "--skip-build-check "
428 "--outdir %s" % self.resultdir)
429 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
431 def test_build_rootfs_short(self):
433 runCmd("wic create wictestdisk "
434 "--image-name=core-image-minimal "
435 "-f -o %s" % self.resultdir)
436 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
438 def test_build_rootfs_long(self):
439 """Test --build-rootfs option"""
440 runCmd("wic create wictestdisk "
441 "--image-name=core-image-minimal "
443 "--outdir %s" % self.resultdir)
444 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
446 # TODO this doesn't have to be x86-specific
447 @skipIfNotArch(['i586', 'i686', 'x86_64'])
448 def test_rootfs_indirect_recipes(self):
449 """Test usage of rootfs plugin with rootfs recipes"""
450 runCmd("wic create directdisk-multi-rootfs "
451 "--image-name=core-image-minimal "
452 "--rootfs rootfs1=core-image-minimal "
453 "--rootfs rootfs2=core-image-minimal "
454 "--outdir %s" % self.resultdir)
455 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "directdisk-multi-rootfs*.direct"))))
457 # TODO this doesn't have to be x86-specific
458 @skipIfNotArch(['i586', 'i686', 'x86_64'])
459 def test_rootfs_artifacts(self):
460 """Test usage of rootfs plugin with rootfs paths"""
461 bb_vars = get_bb_vars(['STAGING_DATADIR', 'RECIPE_SYSROOT_NATIVE'],
463 bb_vars.update(get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_ROOTFS'],
464 'core-image-minimal'))
465 bbvars = {key.lower(): value for key, value in bb_vars.items()}
466 bbvars['wks'] = "directdisk-multi-rootfs"
467 bbvars['resultdir'] = self.resultdir
468 runCmd("wic create %(wks)s "
469 "--bootimg-dir=%(staging_datadir)s "
470 "--kernel-dir=%(deploy_dir_image)s "
471 "--native-sysroot=%(recipe_sysroot_native)s "
472 "--rootfs-dir rootfs1=%(image_rootfs)s "
473 "--rootfs-dir rootfs2=%(image_rootfs)s "
474 "--outdir %(resultdir)s" % bbvars)
475 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "%(wks)s-*.direct" % bbvars))))
477 def test_exclude_path(self):
478 """Test --exclude-path wks option."""
480 oldpath = os.environ['PATH']
481 os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
484 wks_file = 'temp.wks'
485 with open(wks_file, 'w') as wks:
486 rootfs_dir = get_bb_var('IMAGE_ROOTFS', 'core-image-minimal')
488 part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path usr
489 part /usr --source rootfs --ondisk mmcblk0 --fstype=ext4 --rootfs-dir %s/usr
490 part /etc --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path bin/ --rootfs-dir %s/usr
491 part /mnt --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path bin/whoami --rootfs-dir %s/usr"""
492 % (rootfs_dir, rootfs_dir, rootfs_dir))
493 runCmd("wic create %s -e core-image-minimal -o %s" \
494 % (wks_file, self.resultdir))
497 wicout = glob(os.path.join(self.resultdir, "%s-*direct" % 'temp'))
498 self.assertEqual(1, len(wicout))
502 # verify partition size with wic
503 res = runCmd("parted -m %s unit b p" % wicimg, stderr=subprocess.PIPE)
505 # parse parted output which looks like this:
507 # /var/tmp/wic/build/tmpfwvjjkf_-201611101222-hda.direct:200MiB:file:512:512:msdos::;\n
508 # 1:0.00MiB:200MiB:200MiB:ext4::;\n
509 partlns = res.output.splitlines()[2:]
511 self.assertEqual(4, len(partlns))
513 for part in [1, 2, 3, 4]:
514 part_file = os.path.join(self.resultdir, "selftest_img.part%d" % part)
515 partln = partlns[part-1].split(":")
516 self.assertEqual(7, len(partln))
517 start = int(partln[1].rstrip("B")) / 512
518 length = int(partln[3].rstrip("B")) / 512
519 runCmd("dd if=%s of=%s skip=%d count=%d" %
520 (wicimg, part_file, start, length))
522 # Test partition 1, should contain the normal root directories, except
524 res = runCmd("debugfs -R 'ls -p' %s" % \
525 os.path.join(self.resultdir, "selftest_img.part1"), stderr=subprocess.PIPE)
526 files = extract_files(res.output)
527 self.assertIn("etc", files)
528 self.assertNotIn("usr", files)
530 # Partition 2, should contain common directories for /usr, not root
532 res = runCmd("debugfs -R 'ls -p' %s" % \
533 os.path.join(self.resultdir, "selftest_img.part2"), stderr=subprocess.PIPE)
534 files = extract_files(res.output)
535 self.assertNotIn("etc", files)
536 self.assertNotIn("usr", files)
537 self.assertIn("share", files)
539 # Partition 3, should contain the same as partition 2, including the bin
540 # directory, but not the files inside it.
541 res = runCmd("debugfs -R 'ls -p' %s" % \
542 os.path.join(self.resultdir, "selftest_img.part3"), stderr=subprocess.PIPE)
543 files = extract_files(res.output)
544 self.assertNotIn("etc", files)
545 self.assertNotIn("usr", files)
546 self.assertIn("share", files)
547 self.assertIn("bin", files)
548 res = runCmd("debugfs -R 'ls -p bin' %s" % \
549 os.path.join(self.resultdir, "selftest_img.part3"), stderr=subprocess.PIPE)
550 files = extract_files(res.output)
551 self.assertIn(".", files)
552 self.assertIn("..", files)
553 self.assertEqual(2, len(files))
555 # Partition 4, should contain the same as partition 2, including the bin
556 # directory, but not whoami (a symlink to busybox.nosuid) inside it.
557 res = runCmd("debugfs -R 'ls -p' %s" % \
558 os.path.join(self.resultdir, "selftest_img.part4"), stderr=subprocess.PIPE)
559 files = extract_files(res.output)
560 self.assertNotIn("etc", files)
561 self.assertNotIn("usr", files)
562 self.assertIn("share", files)
563 self.assertIn("bin", files)
564 res = runCmd("debugfs -R 'ls -p bin' %s" % \
565 os.path.join(self.resultdir, "selftest_img.part4"), stderr=subprocess.PIPE)
566 files = extract_files(res.output)
567 self.assertIn(".", files)
568 self.assertIn("..", files)
569 self.assertIn("who", files)
570 self.assertNotIn("whoami", files)
572 for part in [1, 2, 3, 4]:
573 part_file = os.path.join(self.resultdir, "selftest_img.part%d" % part)
577 os.environ['PATH'] = oldpath
579 def test_exclude_path_with_extra_space(self):
580 """Test having --exclude-path with IMAGE_ROOTFS_EXTRA_SPACE. [Yocto #15555]"""
582 with NamedTemporaryFile("w", suffix=".wks") as wks:
584 ['bootloader --ptable gpt\n',
585 'part /boot --size=100M --active --fstype=ext4 --label boot\n',
586 'part / --source rootfs --fstype=ext4 --label root --exclude-path boot/\n'])
588 config = 'IMAGE_ROOTFS_EXTRA_SPACE = "500000"\n'\
589 'DEPENDS:pn-core-image-minimal += "wic-tools"\n'\
590 'IMAGE_FSTYPES += "wic ext4"\n'\
591 'WKS_FILE = "%s"\n' % wks.name
592 self.append_config(config)
593 bitbake('core-image-minimal')
596 the output of "wic ls <image>.wic" will look something like:
597 Num Start End Size Fstype
598 1 17408 136332287 136314880 ext4
599 2 136332288 171464703 35132416 ext4
600 we are looking for the size of partition 2
601 i.e. in this case the number 35,132,416
602 without the fix the size will be around 85,403,648
603 with the fix the size should be around 799,960,064
605 bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'MACHINE'], 'core-image-minimal')
606 deploy_dir = bb_vars['DEPLOY_DIR_IMAGE']
607 machine = bb_vars['MACHINE']
608 nativesysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
609 wicout = glob(os.path.join(deploy_dir, "core-image-minimal-%s.rootfs-*.wic" % machine))[0]
610 size_of_root_partition = int(runCmd("wic ls %s --native-sysroot %s" % (wicout, nativesysroot)).output.split('\n')[2].split()[3])
611 self.assertGreater(size_of_root_partition, 500000000)
613 def test_include_path(self):
614 """Test --include-path wks option."""
616 oldpath = os.environ['PATH']
617 os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
620 include_path = os.path.join(self.resultdir, 'test-include')
621 os.makedirs(include_path)
622 with open(os.path.join(include_path, 'test-file'), 'w') as t:
624 wks_file = os.path.join(include_path, 'temp.wks')
625 with open(wks_file, 'w') as wks:
626 rootfs_dir = get_bb_var('IMAGE_ROOTFS', 'core-image-minimal')
628 part /part1 --source rootfs --ondisk mmcblk0 --fstype=ext4
629 part /part2 --source rootfs --ondisk mmcblk0 --fstype=ext4 --include-path %s"""
631 runCmd("wic create %s -e core-image-minimal -o %s" \
632 % (wks_file, self.resultdir))
634 part1 = glob(os.path.join(self.resultdir, 'temp-*.direct.p1'))[0]
635 part2 = glob(os.path.join(self.resultdir, 'temp-*.direct.p2'))[0]
637 # Test partition 1, should not contain 'test-file'
638 res = runCmd("debugfs -R 'ls -p' %s" % (part1), stderr=subprocess.PIPE)
639 files = extract_files(res.output)
640 self.assertNotIn('test-file', files)
641 self.assertEqual(True, files_own_by_root(res.output))
643 # Test partition 2, should contain 'test-file'
644 res = runCmd("debugfs -R 'ls -p' %s" % (part2), stderr=subprocess.PIPE)
645 files = extract_files(res.output)
646 self.assertIn('test-file', files)
647 self.assertEqual(True, files_own_by_root(res.output))
650 os.environ['PATH'] = oldpath
652 def test_include_path_embeded(self):
653 """Test --include-path wks option."""
655 oldpath = os.environ['PATH']
656 os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
659 include_path = os.path.join(self.resultdir, 'test-include')
660 os.makedirs(include_path)
661 with open(os.path.join(include_path, 'test-file'), 'w') as t:
663 wks_file = os.path.join(include_path, 'temp.wks')
664 with open(wks_file, 'w') as wks:
666 part / --source rootfs --fstype=ext4 --include-path %s --include-path core-image-minimal-mtdutils export/"""
668 runCmd("wic create %s -e core-image-minimal -o %s" \
669 % (wks_file, self.resultdir))
671 part1 = glob(os.path.join(self.resultdir, 'temp-*.direct.p1'))[0]
673 res = runCmd("debugfs -R 'ls -p' %s" % (part1), stderr=subprocess.PIPE)
674 files = extract_files(res.output)
675 self.assertIn('test-file', files)
676 self.assertEqual(True, files_own_by_root(res.output))
678 res = runCmd("debugfs -R 'ls -p /export/etc/' %s" % (part1), stderr=subprocess.PIPE)
679 files = extract_files(res.output)
680 self.assertIn('passwd', files)
681 self.assertEqual(True, files_own_by_root(res.output))
684 os.environ['PATH'] = oldpath
686 def test_include_path_errors(self):
687 """Test --include-path wks option error handling."""
688 wks_file = 'temp.wks'
691 with open(wks_file, 'w') as wks:
692 wks.write("part / --source rootfs --fstype=ext4 --include-path core-image-minimal-mtdutils /export")
693 self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
694 % (wks_file, self.resultdir), ignore_status=True).status)
697 # Argument pointing to parent directory.
698 with open(wks_file, 'w') as wks:
699 wks.write("part / --source rootfs --fstype=ext4 --include-path core-image-minimal-mtdutils ././..")
700 self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
701 % (wks_file, self.resultdir), ignore_status=True).status)
704 # 3 Argument pointing to parent directory.
705 with open(wks_file, 'w') as wks:
706 wks.write("part / --source rootfs --fstype=ext4 --include-path core-image-minimal-mtdutils export/ dummy")
707 self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
708 % (wks_file, self.resultdir), ignore_status=True).status)
711 def test_exclude_path_errors(self):
712 """Test --exclude-path wks option error handling."""
713 wks_file = 'temp.wks'
716 with open(wks_file, 'w') as wks:
717 wks.write("part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path /usr")
718 self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
719 % (wks_file, self.resultdir), ignore_status=True).status)
722 # Argument pointing to parent directory.
723 with open(wks_file, 'w') as wks:
724 wks.write("part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path ././..")
725 self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
726 % (wks_file, self.resultdir), ignore_status=True).status)
729 def test_permissions(self):
730 """Test permissions are respected"""
732 # prepare wicenv and rootfs
733 bitbake('core-image-minimal core-image-minimal-mtdutils -c do_rootfs_wicenv')
735 oldpath = os.environ['PATH']
736 os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
739 part / --source rootfs --fstype=ext4
742 part / --source rootfs --fstype=ext4 --exclude-path=home
745 part / --source rootfs --ondisk sda --fstype=ext4
746 part /export --source rootfs --rootfs=core-image-minimal-mtdutils --fstype=ext4
749 part / --source rootfs --ondisk sda --fstype=ext4 --exclude-path=etc/Â Â
750 part /etc --source rootfs --fstype=ext4 --change-directory=etc
752 tests = [t_normal, t_exclude, t_multi, t_change]
756 include_path = os.path.join(self.resultdir, 'test-include')
757 os.makedirs(include_path)
758 wks_file = os.path.join(include_path, 'temp.wks')
759 with open(wks_file, 'w') as wks:
761 runCmd("wic create %s -e core-image-minimal -o %s" \
762 % (wks_file, self.resultdir))
764 for part in glob(os.path.join(self.resultdir, 'temp-*.direct.p*')):
765 res = runCmd("debugfs -R 'ls -p' %s" % (part), stderr=subprocess.PIPE)
766 self.assertEqual(True, files_own_by_root(res.output))
768 config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "%s"\n' % wks_file
769 self.append_config(config)
770 bitbake('core-image-minimal')
771 tmpdir = os.path.join(get_bb_var('WORKDIR', 'core-image-minimal'),'build-wic')
773 # check each partition for permission
774 for part in glob(os.path.join(tmpdir, 'temp-*.direct.p*')):
775 res = runCmd("debugfs -R 'ls -p' %s" % (part), stderr=subprocess.PIPE)
776 self.assertTrue(files_own_by_root(res.output)
777 ,msg='Files permission incorrect using wks set "%s"' % test)
779 # clean config and result directory for next cases
780 self.remove_config(config)
781 rmtree(self.resultdir, ignore_errors=True)
784 os.environ['PATH'] = oldpath
786 def test_change_directory(self):
787 """Test --change-directory wks option."""
789 oldpath = os.environ['PATH']
790 os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
793 include_path = os.path.join(self.resultdir, 'test-include')
794 os.makedirs(include_path)
795 wks_file = os.path.join(include_path, 'temp.wks')
796 with open(wks_file, 'w') as wks:
797 wks.write("part /etc --source rootfs --fstype=ext4 --change-directory=etc")
798 runCmd("wic create %s -e core-image-minimal -o %s" \
799 % (wks_file, self.resultdir))
801 part1 = glob(os.path.join(self.resultdir, 'temp-*.direct.p1'))[0]
803 res = runCmd("debugfs -R 'ls -p' %s" % (part1), stderr=subprocess.PIPE)
804 files = extract_files(res.output)
805 self.assertIn('passwd', files)
808 os.environ['PATH'] = oldpath
810 def test_change_directory_errors(self):
811 """Test --change-directory wks option error handling."""
812 wks_file = 'temp.wks'
815 with open(wks_file, 'w') as wks:
816 wks.write("part / --source rootfs --fstype=ext4 --change-directory /usr")
817 self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
818 % (wks_file, self.resultdir), ignore_status=True).status)
821 # Argument pointing to parent directory.
822 with open(wks_file, 'w') as wks:
823 wks.write("part / --source rootfs --fstype=ext4 --change-directory ././..")
824 self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
825 % (wks_file, self.resultdir), ignore_status=True).status)
828 def test_no_fstab_update(self):
829 """Test --no-fstab-update wks option."""
831 oldpath = os.environ['PATH']
832 os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
834 # Get stock fstab from base-files recipe
835 bitbake('base-files -c do_install')
836 bf_fstab = os.path.join(get_bb_var('D', 'base-files'), 'etc', 'fstab')
837 self.assertEqual(True, os.path.exists(bf_fstab))
838 bf_fstab_md5sum = runCmd('md5sum %s ' % bf_fstab).output.split(" ")[0]
841 no_fstab_update_path = os.path.join(self.resultdir, 'test-no-fstab-update')
842 os.makedirs(no_fstab_update_path)
843 wks_file = os.path.join(no_fstab_update_path, 'temp.wks')
844 with open(wks_file, 'w') as wks:
845 wks.writelines(['part / --source rootfs --fstype=ext4 --label rootfs\n',
846 'part /mnt/p2 --source rootfs --rootfs-dir=core-image-minimal ',
847 '--fstype=ext4 --label p2 --no-fstab-update\n'])
848 runCmd("wic create %s -e core-image-minimal -o %s" \
849 % (wks_file, self.resultdir))
851 part_fstab_md5sum = []
852 for i in range(1, 3):
853 part = glob(os.path.join(self.resultdir, 'temp-*.direct.p') + str(i))[0]
854 part_fstab = runCmd("debugfs -R 'cat etc/fstab' %s" % (part), stderr=subprocess.PIPE)
855 part_fstab_md5sum.append(hashlib.md5((part_fstab.output + "\n\n").encode('utf-8')).hexdigest())
857 # '/etc/fstab' in partition 2 should contain the same stock fstab file
858 # as the one installed by the base-file recipe.
859 self.assertEqual(bf_fstab_md5sum, part_fstab_md5sum[1])
861 # '/etc/fstab' in partition 1 should contain an updated fstab file.
862 self.assertNotEqual(bf_fstab_md5sum, part_fstab_md5sum[0])
865 os.environ['PATH'] = oldpath
867 def test_no_fstab_update_errors(self):
868 """Test --no-fstab-update wks option error handling."""
869 wks_file = 'temp.wks'
872 with open(wks_file, 'w') as wks:
873 wks.write("part / --source rootfs --fstype=ext4 --no-fstab-update /etc")
874 self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
875 % (wks_file, self.resultdir), ignore_status=True).status)
878 # Argument pointing to parent directory.
879 with open(wks_file, 'w') as wks:
880 wks.write("part / --source rootfs --fstype=ext4 --no-fstab-update ././..")
881 self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
882 % (wks_file, self.resultdir), ignore_status=True).status)
885 def test_extra_space(self):
886 """Test --extra-space wks option."""
888 runCmd("wic create wictestdisk "
889 "--image-name core-image-minimal "
890 "--extra-space %i -o %s" % (extraspace ,self.resultdir))
891 wicout = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
892 self.assertEqual(1, len(wicout))
893 size = os.path.getsize(wicout[0])
894 self.assertTrue(size > extraspace, msg="Extra space not present (%s vs %s)" % (size, extraspace))
896 def test_no_table(self):
897 """Test --no-table wks option."""
898 wks_file = 'temp.wks'
901 with open(wks_file, 'w') as wks:
902 wks.write("part testspace --no-table --fixed-size 16k --offset 4080k")
903 runCmd("wic create %s --image-name core-image-minimal -o %s" % (wks_file, self.resultdir))
905 wicout = glob(os.path.join(self.resultdir, "*.*"))
907 self.assertEqual(1, len(wicout))
908 size = os.path.getsize(wicout[0])
909 self.assertEqual(size, 4 * 1024 * 1024)
913 def test_partition_hidden_attributes(self):
914 """Test --hidden wks option."""
915 wks_file = 'temp.wks'
916 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
918 with open(wks_file, 'w') as wks:
920 part / --source rootfs --fstype=ext4
921 part / --source rootfs --fstype=ext4 --hidden
922 bootloader --ptable gpt""")
924 runCmd("wic create %s -e core-image-minimal -o %s" \
925 % (wks_file, self.resultdir))
926 wicout = os.path.join(self.resultdir, "*.direct")
928 result = runCmd("%s/usr/sbin/sfdisk --part-attrs %s 1" % (sysroot, wicout))
929 self.assertEqual('', result.output)
930 result = runCmd("%s/usr/sbin/sfdisk --part-attrs %s 2" % (sysroot, wicout))
931 self.assertEqual('RequiredPartition', result.output)
936 def test_wic_sector_size(self):
937 """Test generation image sector size"""
939 oldpath = os.environ['PATH']
940 os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
943 # Add WIC_SECTOR_SIZE into config
944 config = 'WIC_SECTOR_SIZE = "4096"\n'\
945 'WICVARS:append = " WIC_SECTOR_SIZE"\n'
946 self.append_config(config)
947 bitbake('core-image-minimal')
949 # Check WIC_SECTOR_SIZE apply to bitbake variable
950 wic_sector_size_str = get_bb_var('WIC_SECTOR_SIZE', 'core-image-minimal')
951 wic_sector_size = int(wic_sector_size_str)
952 self.assertEqual(4096, wic_sector_size)
954 self.logger.info("Test wic_sector_size: %d \n" % wic_sector_size)
956 with NamedTemporaryFile("w", suffix=".wks") as wks:
958 ['bootloader --ptable gpt\n',
959 'part --fstype ext4 --source rootfs --label rofs-a --mkfs-extraopts "-b 4096"\n',
960 'part --fstype ext4 --source rootfs --use-uuid --mkfs-extraopts "-b 4096"\n'])
962 cmd = "wic create %s -e core-image-minimal -o %s" % (wks.name, self.resultdir)
964 wksname = os.path.splitext(os.path.basename(wks.name))[0]
965 images = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
966 self.assertEqual(1, len(images))
968 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
970 result = runCmd("wic ls %s -n %s" % (images[0], sysroot))
971 self.assertEqual(3, len(result.output.split('\n')))
973 # verify partition size with wic
974 res = runCmd("export PARTED_SECTOR_SIZE=%d; parted -m %s unit b p" % (wic_sector_size, images[0]),
975 stderr=subprocess.PIPE)
977 # parse parted output which looks like this:
979 # /var/tmp/wic/build/tmpgjzzefdd-202410281021-sda.direct:78569472B:file:4096:4096:gpt::;\n
980 # 1:139264B:39284735B:39145472B:ext4:rofs-a:;\n
981 # 2:39284736B:78430207B:39145472B:ext4:primary:;\n
982 disk_info = res.output.splitlines()[1]
984 sector_size_logical = int(disk_info.split(":")[3])
985 sector_size_physical = int(disk_info.split(":")[4])
986 self.assertEqual(wic_sector_size, sector_size_logical, "Logical sector size is not %d." % wic_sector_size)
987 self.assertEqual(wic_sector_size, sector_size_physical, "Physical sector size is not %d." % wic_sector_size)
990 os.environ['PATH'] = oldpath
992 class Wic2(WicTestCase):
994 def test_bmap_short(self):
995 """Test generation of .bmap file -m option"""
996 cmd = "wic create wictestdisk -e core-image-minimal -m -o %s" % self.resultdir
998 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct"))))
999 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct.bmap"))))
1001 def test_bmap_long(self):
1002 """Test generation of .bmap file --bmap option"""
1003 cmd = "wic create wictestdisk -e core-image-minimal --bmap -o %s" % self.resultdir
1005 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct"))))
1006 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct.bmap"))))
1008 def test_image_env(self):
1009 """Test generation of <image>.env files."""
1010 image = 'core-image-minimal'
1011 imgdatadir = self._get_image_env_path(image)
1013 bb_vars = get_bb_vars(['IMAGE_BASENAME', 'WICVARS'], image)
1014 basename = bb_vars['IMAGE_BASENAME']
1015 self.assertEqual(basename, image)
1016 path = os.path.join(imgdatadir, basename) + '.env'
1017 self.assertTrue(os.path.isfile(path), msg="File %s wasn't generated as expected" % path)
1019 wicvars = set(bb_vars['WICVARS'].split())
1020 # filter out optional variables
1021 wicvars = wicvars.difference(('DEPLOY_DIR_IMAGE', 'IMAGE_BOOT_FILES',
1022 'INITRD', 'INITRD_LIVE', 'ISODIR','INITRAMFS_IMAGE',
1023 'INITRAMFS_IMAGE_BUNDLE', 'INITRAMFS_LINK_NAME',
1024 'APPEND', 'IMAGE_EFI_BOOT_FILES'))
1025 with open(path) as envfile:
1026 content = dict(line.split("=", 1) for line in envfile)
1027 # test if variables used by wic present in the .env file
1029 self.assertTrue(var in content, "%s is not in .env file" % var)
1030 self.assertTrue(content[var], "%s doesn't have a value (%s)" % (var, content[var]))
1032 def test_image_vars_dir_short(self):
1033 """Test image vars directory selection -v option"""
1034 image = 'core-image-minimal'
1035 imgenvdir = self._get_image_env_path(image)
1036 native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
1038 runCmd("wic create wictestdisk "
1039 "--image-name=%s -v %s -n %s -o %s"
1040 % (image, imgenvdir, native_sysroot,
1042 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct"))))
1044 def test_image_vars_dir_long(self):
1045 """Test image vars directory selection --vars option"""
1046 image = 'core-image-minimal'
1047 imgenvdir = self._get_image_env_path(image)
1048 native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
1050 runCmd("wic create wictestdisk "
1053 "--native-sysroot %s "
1055 % (image, imgenvdir, native_sysroot,
1057 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct"))))
1059 # TODO this test could also work on aarch64
1060 @skipIfNotArch(['i586', 'i686', 'x86_64'])
1061 def test_wic_image_type(self):
1062 """Test building wic images by bitbake"""
1063 config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "wic-image-minimal"\n'\
1064 'MACHINE_FEATURES:append = " efi"\n'
1065 image_recipe_append = """
1066 do_image_wic[postfuncs] += "run_wic_cmd"
1068 echo "test" >> ${WORKDIR}/test.wic-cp
1069 wic cp --vars "${STAGING_DIR}/${MACHINE}/imgdata/" -e "${IMAGE_BASENAME}" ${WORKDIR}/test.wic-cp ${IMGDEPLOYDIR}/${IMAGE_NAME}.wic:1/
1070 wic ls --vars "${STAGING_DIR}/${MACHINE}/imgdata/" -e "${IMAGE_BASENAME}" ${IMGDEPLOYDIR}/${IMAGE_NAME}.wic:1/
1071 wic rm --vars "${STAGING_DIR}/${MACHINE}/imgdata/" -e "${IMAGE_BASENAME}" ${IMGDEPLOYDIR}/${IMAGE_NAME}.wic:1/test.wic-cp
1072 wic cp --vars "${STAGING_DIR}/${MACHINE}/imgdata/" -e "${IMAGE_BASENAME}" ${WORKDIR}/test.wic-cp ${IMGDEPLOYDIR}/${IMAGE_NAME}.wic:1/
1075 self.write_recipeinc('images', image_recipe_append)
1077 self.append_config(config)
1078 image = 'wic-image-minimal'
1080 self.remove_config(config)
1082 bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image)
1083 prefix = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], '%s.' % bb_vars['IMAGE_LINK_NAME'])
1085 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1086 # check if file is there
1087 result = runCmd("wic ls %s:1/ -n %s" % (prefix+"wic", sysroot))
1088 self.assertIn("test.wic-cp", result.output)
1090 # check if we have result image and manifests symlinks
1091 # pointing to existing files
1092 for suffix in ('wic', 'manifest'):
1093 path = prefix + suffix
1094 self.assertTrue(os.path.islink(path), msg="Link %s wasn't generated as expected" % path)
1095 self.assertTrue(os.path.isfile(os.path.realpath(path)), msg="File linked to by %s wasn't generated as expected" % path)
1097 # TODO this should work on aarch64
1098 @skipIfNotArch(['i586', 'i686', 'x86_64'])
1099 @OETestTag("runqemu")
1100 def test_qemu(self):
1101 """Test wic-image-minimal under qemu"""
1102 config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "wic-image-minimal"\n'\
1103 'MACHINE_FEATURES:append = " efi"\n'
1104 self.append_config(config)
1105 image_recipe_append = """
1106 do_image_wic[postfuncs] += "run_wic_cmd"
1108 echo "test" >> ${WORKDIR}/test.wic-cp
1109 wic cp --vars "${STAGING_DIR}/${MACHINE}/imgdata/" -e "${IMAGE_BASENAME}" ${WORKDIR}/test.wic-cp ${IMGDEPLOYDIR}/${IMAGE_NAME}.wic:1/
1110 wic ls --vars "${STAGING_DIR}/${MACHINE}/imgdata/" -e "${IMAGE_BASENAME}" ${IMGDEPLOYDIR}/${IMAGE_NAME}.wic:1/
1111 wic rm --vars "${STAGING_DIR}/${MACHINE}/imgdata/" -e "${IMAGE_BASENAME}" ${IMGDEPLOYDIR}/${IMAGE_NAME}.wic:1/test.wic-cp
1112 wic cp --vars "${STAGING_DIR}/${MACHINE}/imgdata/" -e "${IMAGE_BASENAME}" ${WORKDIR}/test.wic-cp ${IMGDEPLOYDIR}/${IMAGE_NAME}.wic:1/
1115 self.write_recipeinc('images', image_recipe_append)
1116 bitbake('wic-image-minimal')
1118 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1119 bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], "wic-image-minimal")
1120 image_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], bb_vars['IMAGE_LINK_NAME'])
1121 # check if file is there
1122 result = runCmd("wic ls %s:1/ -n %s" % (image_path+".wic", sysroot))
1123 self.assertIn("test.wic-cp", result.output)
1124 self.remove_config(config)
1126 runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'wic-image-minimal') or ""
1127 with runqemu('wic-image-minimal', ssh=False, runqemuparams='%s nographic' % (runqemu_params)) as qemu:
1128 cmd = "mount | grep '^/dev/' | cut -f1,3 -d ' ' | egrep -c -e '/dev/sda1 /boot' " \
1129 "-e '/dev/root /|/dev/sda2 /' -e '/dev/sda3 /media' -e '/dev/sda4 /mnt'"
1130 status, output = qemu.run_serial(cmd)
1131 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1132 self.assertEqual(output, '4')
1133 cmd = "grep UUID=2c71ef06-a81d-4735-9d3a-379b69c6bdba /etc/fstab"
1134 status, output = qemu.run_serial(cmd)
1135 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1136 self.assertEqual(output, 'UUID=2c71ef06-a81d-4735-9d3a-379b69c6bdba\t/media\text4\tdefaults\t0\t0')
1138 @skipIfNotArch(['i586', 'i686', 'x86_64'])
1139 @OETestTag("runqemu")
1140 def test_qemu_efi(self):
1141 """Test core-image-minimal efi image under qemu"""
1142 config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "mkefidisk.wks"\n'
1143 self.append_config(config)
1144 bitbake('core-image-minimal ovmf')
1145 self.remove_config(config)
1147 runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-minimal') or ""
1148 with runqemu('core-image-minimal', ssh=False,
1149 runqemuparams='%s nographic ovmf' % (runqemu_params), image_fstype='wic') as qemu:
1150 cmd = "grep sda. /proc/partitions |wc -l"
1151 status, output = qemu.run_serial(cmd)
1152 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1153 self.assertEqual(output, '3')
1156 def _make_fixed_size_wks(size):
1158 Create a wks of an image with a single partition. Size of the partition is set
1159 using --fixed-size flag. Returns a tuple: (path to wks file, wks image name)
1161 with NamedTemporaryFile("w", suffix=".wks", delete=False) as tempf:
1162 wkspath = tempf.name
1163 tempf.write("part " \
1164 "--source rootfs --ondisk hda --align 4 --fixed-size %d "
1165 "--fstype=ext4\n" % size)
1169 def _get_wic(self, wkspath, ignore_status=False):
1170 p = runCmd("wic create %s -e core-image-minimal -o %s" % (wkspath, self.resultdir),
1171 ignore_status=ignore_status)
1176 wksname = os.path.splitext(os.path.basename(wkspath))[0]
1178 wicout = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1183 return (p, wicout[0])
1185 def _get_wic_partitions(self, wkspath, native_sysroot=None, ignore_status=False):
1186 p, wicimg = self._get_wic(wkspath, ignore_status)
1191 if not native_sysroot:
1192 native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
1194 # verify partition size with wic
1195 res = runCmd("parted -m %s unit kib p" % wicimg,
1196 native_sysroot=native_sysroot, stderr=subprocess.PIPE)
1198 # parse parted output which looks like this:
1200 # /var/tmp/wic/build/tmpfwvjjkf_-201611101222-hda.direct:200MiB:file:512:512:msdos::;\n
1201 # 1:0.00MiB:200MiB:200MiB:ext4::;\n
1202 return (p, res.output.splitlines()[2:])
1204 def test_fixed_size(self):
1206 Test creation of a simple image with partition size controlled through
1209 wkspath = Wic2._make_fixed_size_wks(200)
1210 _, partlns = self._get_wic_partitions(wkspath)
1213 self.assertEqual(partlns, [
1214 "1:4.00kiB:204804kiB:204800kiB:ext4::;",
1217 def test_fixed_size_error(self):
1219 Test creation of a simple image with partition size controlled through
1220 --fixed-size flag. The size of partition is intentionally set to 1MiB
1221 in order to trigger an error in wic.
1223 wkspath = Wic2._make_fixed_size_wks(1)
1224 p, _ = self._get_wic_partitions(wkspath, ignore_status=True)
1227 self.assertNotEqual(p.status, 0, "wic exited successfully when an error was expected:\n%s" % p.output)
1229 def test_offset(self):
1230 native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
1232 with NamedTemporaryFile("w", suffix=".wks") as tempf:
1233 # Test that partitions are placed at the correct offsets, default KB
1234 tempf.write("bootloader --ptable gpt\n" \
1235 "part / --source rootfs --ondisk hda --offset 32 --fixed-size 200M --fstype=ext4\n" \
1236 "part /bar --ondisk hda --offset 204832 --fixed-size 100M --fstype=ext4\n")
1239 _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
1240 self.assertEqual(partlns, [
1241 "1:32.0kiB:204832kiB:204800kiB:ext4:primary:;",
1242 "2:204832kiB:307232kiB:102400kiB:ext4:primary:;",
1245 with NamedTemporaryFile("w", suffix=".wks") as tempf:
1246 # Test that partitions are placed at the correct offsets, same with explicit KB
1247 tempf.write("bootloader --ptable gpt\n" \
1248 "part / --source rootfs --ondisk hda --offset 32K --fixed-size 200M --fstype=ext4\n" \
1249 "part /bar --ondisk hda --offset 204832K --fixed-size 100M --fstype=ext4\n")
1252 _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
1253 self.assertEqual(partlns, [
1254 "1:32.0kiB:204832kiB:204800kiB:ext4:primary:;",
1255 "2:204832kiB:307232kiB:102400kiB:ext4:primary:;",
1258 with NamedTemporaryFile("w", suffix=".wks") as tempf:
1259 # Test that partitions are placed at the correct offsets using MB
1260 tempf.write("bootloader --ptable gpt\n" \
1261 "part / --source rootfs --ondisk hda --offset 32K --fixed-size 200M --fstype=ext4\n" \
1262 "part /bar --ondisk hda --offset 201M --fixed-size 100M --fstype=ext4\n")
1265 _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
1266 self.assertEqual(partlns, [
1267 "1:32.0kiB:204832kiB:204800kiB:ext4:primary:;",
1268 "2:205824kiB:308224kiB:102400kiB:ext4:primary:;",
1271 with NamedTemporaryFile("w", suffix=".wks") as tempf:
1272 # Test that partitions can be placed on a 512 byte sector boundary
1273 tempf.write("bootloader --ptable gpt\n" \
1274 "part / --source rootfs --ondisk hda --offset 65s --fixed-size 199M --fstype=ext4\n" \
1275 "part /bar --ondisk hda --offset 204832 --fixed-size 100M --fstype=ext4\n")
1278 _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
1279 self.assertEqual(partlns, [
1280 "1:32.5kiB:203808kiB:203776kiB:ext4:primary:;",
1281 "2:204832kiB:307232kiB:102400kiB:ext4:primary:;",
1284 with NamedTemporaryFile("w", suffix=".wks") as tempf:
1285 # Test that a partition can be placed immediately after a MSDOS partition table
1286 tempf.write("bootloader --ptable msdos\n" \
1287 "part / --source rootfs --ondisk hda --offset 1s --fixed-size 200M --fstype=ext4\n")
1290 _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
1291 self.assertEqual(partlns, [
1292 "1:0.50kiB:204800kiB:204800kiB:ext4::;",
1295 with NamedTemporaryFile("w", suffix=".wks") as tempf:
1296 # Test that image creation fails if the partitions would overlap
1297 tempf.write("bootloader --ptable gpt\n" \
1298 "part / --source rootfs --ondisk hda --offset 32 --fixed-size 200M --fstype=ext4\n" \
1299 "part /bar --ondisk hda --offset 204831 --fixed-size 100M --fstype=ext4\n")
1302 p, _ = self._get_wic_partitions(tempf.name, ignore_status=True)
1303 self.assertNotEqual(p.status, 0, "wic exited successfully when an error was expected:\n%s" % p.output)
1305 with NamedTemporaryFile("w", suffix=".wks") as tempf:
1306 # Test that partitions are not allowed to overlap with the booloader
1307 tempf.write("bootloader --ptable gpt\n" \
1308 "part / --source rootfs --ondisk hda --offset 8 --fixed-size 200M --fstype=ext4\n")
1311 p, _ = self._get_wic_partitions(tempf.name, ignore_status=True)
1312 self.assertNotEqual(p.status, 0, "wic exited successfully when an error was expected:\n%s" % p.output)
1314 def test_extra_filesystem_space(self):
1315 native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
1317 with NamedTemporaryFile("w", suffix=".wks") as tempf:
1318 tempf.write("bootloader --ptable gpt\n" \
1319 "part / --source rootfs --ondisk hda --extra-filesystem-space 200M --fstype=ext4\n")
1322 _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
1323 self.assertEqual(len(partlns), 1)
1324 size = partlns[0].split(':')[3]
1325 self.assertRegex(size, r'^[0-9]+kiB$')
1326 size = int(size[:-3])
1327 self.assertGreaterEqual(size, 204800)
1329 def test_extra_partition_space(self):
1330 native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
1332 with NamedTemporaryFile("w", suffix=".wks") as tempf:
1333 tempf.write("bootloader --ptable gpt\n" \
1334 "part --ondisk hda --size 10M --extra-partition-space 10M --fstype=ext4\n" \
1335 "part --ondisk hda --fixed-size 20M --extra-partition-space 10M --fstype=ext4\n" \
1336 "part --source rootfs --ondisk hda --extra-partition-space 10M --fstype=ext4\n" \
1337 "part --source rootfs --ondisk hda --fixed-size 200M --extra-partition-space 10M --fstype=ext4\n")
1340 _, wicimg = self._get_wic(tempf.name)
1342 res = runCmd("parted -m %s unit b p" % wicimg,
1343 native_sysroot=native_sysroot, stderr=subprocess.PIPE)
1345 # parse parted output which looks like this:
1347 # /var/tmp/wic/build/tmpfwvjjkf_-201611101222-hda.direct:200MiB:file:512:512:msdos::;\n
1348 # 1:0.00MiB:200MiB:200MiB:ext4::;\n
1349 partlns = res.output.splitlines()[2:]
1351 self.assertEqual(4, len(partlns))
1353 # Test for each partitions that the extra part space exists
1354 for part in range(0, len(partlns)):
1355 part_file = os.path.join(self.resultdir, "selftest_img.part%d" % (part + 1))
1356 partln = partlns[part].split(":")
1357 self.assertEqual(7, len(partln))
1358 self.assertRegex(partln[3], r'^[0-9]+B$')
1359 part_size = int(partln[3].rstrip("B"))
1360 start = int(partln[1].rstrip("B")) / 512
1361 length = part_size / 512
1362 runCmd("dd if=%s of=%s skip=%d count=%d" %
1363 (wicimg, part_file, start, length))
1364 res = runCmd("dumpe2fs %s -h | grep \"^Block count\"" % part_file)
1365 fs_size = int(res.output.split(":")[1].strip()) * 1024
1366 self.assertLessEqual(fs_size + 10485760, part_size, "part file: %s" % part_file)
1368 # TODO this test could also work on aarch64
1369 @skipIfNotArch(['i586', 'i686', 'x86_64'])
1370 @OETestTag("runqemu")
1371 def test_rawcopy_plugin_qemu(self):
1372 """Test rawcopy plugin in qemu"""
1373 # build ext4 and then use it for a wic image
1374 config = 'IMAGE_FSTYPES = "ext4"\n'
1375 self.append_config(config)
1376 bitbake('core-image-minimal')
1377 image_link_name = get_bb_var('IMAGE_LINK_NAME', 'core-image-minimal')
1378 self.remove_config(config)
1380 config = 'IMAGE_FSTYPES = "wic"\n' \
1381 'IMAGE_LINK_NAME_CORE_IMAGE_MINIMAL = "%s"\n'\
1382 'WKS_FILE = "test_rawcopy_plugin.wks.in"\n'\
1384 self.append_config(config)
1385 bitbake('core-image-minimal-mtdutils')
1386 self.remove_config(config)
1388 runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-minimal-mtdutils') or ""
1389 with runqemu('core-image-minimal-mtdutils', ssh=False,
1390 runqemuparams='%s nographic' % (runqemu_params), image_fstype='wic') as qemu:
1391 cmd = "grep sda. /proc/partitions |wc -l"
1392 status, output = qemu.run_serial(cmd)
1393 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1394 self.assertEqual(output, '2')
1396 def _rawcopy_plugin(self, fstype):
1397 """Test rawcopy plugin"""
1398 image = 'core-image-minimal'
1399 bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image)
1400 params = ',unpack' if fstype.endswith('.gz') else ''
1401 with NamedTemporaryFile("w", suffix=".wks") as wks:
1402 wks.write('part / --source rawcopy --sourceparams="file=%s.%s%s"\n'\
1403 % (bb_vars['IMAGE_LINK_NAME'], fstype, params))
1405 cmd = "wic create %s -e %s -o %s" % (wks.name, image, self.resultdir)
1407 wksname = os.path.splitext(os.path.basename(wks.name))[0]
1408 out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1409 self.assertEqual(1, len(out))
1411 def test_rawcopy_plugin(self):
1412 config = 'IMAGE_FSTYPES = "ext4"\n'
1413 self.append_config(config)
1414 self.assertEqual(0, bitbake('core-image-minimal').status)
1415 self.remove_config(config)
1416 self._rawcopy_plugin('ext4')
1418 def test_rawcopy_plugin_unpack(self):
1420 config = 'IMAGE_FSTYPES = "%s"\n' % fstype
1421 self.append_config(config)
1422 self.assertEqual(0, bitbake('core-image-minimal').status)
1423 self.remove_config(config)
1424 self._rawcopy_plugin(fstype)
1426 def test_empty_plugin(self):
1427 """Test empty plugin"""
1428 config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "test_empty_plugin.wks"\n'
1429 self.append_config(config)
1430 image = 'core-image-minimal'
1432 self.remove_config(config)
1433 bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image)
1434 image_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], '%s.wic' % bb_vars['IMAGE_LINK_NAME'])
1435 self.assertTrue(os.path.exists(image_path), msg="Image file %s wasn't generated as expected" % image_path)
1437 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1439 # Fstype column from 'wic ls' should be empty for the second partition
1440 # as listed in test_empty_plugin.wks
1441 result = runCmd("wic ls %s -n %s | awk -F ' ' '{print $1 \" \" $5}' | grep '^2' | wc -w" % (image_path, sysroot))
1442 self.assertEqual('1', result.output)
1444 @skipIfNotArch(['i586', 'i686', 'x86_64'])
1445 @OETestTag("runqemu")
1446 def test_biosplusefi_plugin_qemu(self):
1447 """Test biosplusefi plugin in qemu"""
1448 config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "test_biosplusefi_plugin.wks"\nMACHINE_FEATURES:append = " efi"\n'
1449 self.append_config(config)
1450 bitbake('core-image-minimal')
1451 self.remove_config(config)
1453 runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-minimal') or ""
1454 with runqemu('core-image-minimal', ssh=False,
1455 runqemuparams='%s nographic' % (runqemu_params), image_fstype='wic') as qemu:
1456 # Check that we have ONLY two /dev/sda* partitions (/boot and /)
1457 cmd = "grep sda. /proc/partitions | wc -l"
1458 status, output = qemu.run_serial(cmd)
1459 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1460 self.assertEqual(output, '2')
1461 # Check that /dev/sda1 is /boot and that either /dev/root OR /dev/sda2 is /
1462 cmd = "mount | grep '^/dev/' | cut -f1,3 -d ' ' | egrep -c -e '/dev/sda1 /boot' -e '/dev/root /|/dev/sda2 /'"
1463 status, output = qemu.run_serial(cmd)
1464 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1465 self.assertEqual(output, '2')
1466 # Check that /boot has EFI bootx64.efi (required for EFI)
1467 cmd = "ls /boot/EFI/BOOT/bootx64.efi | wc -l"
1468 status, output = qemu.run_serial(cmd)
1469 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1470 self.assertEqual(output, '1')
1471 # Check that "BOOTABLE" flag is set on boot partition (required for PC-Bios)
1472 # Trailing "cat" seems to be required; otherwise run_serial() sends back echo of the input command
1473 cmd = "fdisk -l /dev/sda | grep /dev/sda1 | awk {print'$2'} | cat"
1474 status, output = qemu.run_serial(cmd)
1475 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1476 self.assertEqual(output, '*')
1478 @skipIfNotArch(['i586', 'i686', 'x86_64'])
1479 def test_biosplusefi_plugin(self):
1480 """Test biosplusefi plugin"""
1481 # Wic generation below may fail depending on the order of the unittests
1482 # This is because bootimg_pcbios (that bootimg_biosplusefi uses) generate its MBR inside STAGING_DATADIR directory
1483 # which may or may not exists depending on what was built already
1484 # If an image hasn't been built yet, directory ${STAGING_DATADIR}/syslinux won't exists and _get_bootimg_dir()
1485 # will raise with "Couldn't find correct bootimg_dir"
1486 # The easiest way to work-around this issue is to make sure we already built an image here, hence the bitbake call
1487 config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "test_biosplusefi_plugin.wks"\nMACHINE_FEATURES:append = " efi"\n'
1488 self.append_config(config)
1489 bitbake('core-image-minimal')
1490 self.remove_config(config)
1492 img = 'core-image-minimal'
1493 with NamedTemporaryFile("w", suffix=".wks") as wks:
1494 wks.writelines(['part /boot --active --source bootimg_biosplusefi --sourceparams="loader=grub-efi"\n',
1495 'part / --source rootfs --fstype=ext4 --align 1024 --use-uuid\n'\
1496 'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n'])
1498 cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1500 wksname = os.path.splitext(os.path.basename(wks.name))[0]
1501 out = glob(os.path.join(self.resultdir, "%s-*.direct" % wksname))
1502 self.assertEqual(1, len(out))
1504 @skipIfNotArch(['i586', 'i686', 'x86_64', 'aarch64'])
1505 def test_uefi_kernel(self):
1506 """ Test uefi-kernel in wic """
1507 config = 'IMAGE_EFI_BOOT_FILES="/etc/fstab;testfile"\nIMAGE_FSTYPES = "wic"\nWKS_FILE = "test_uefikernel.wks"\nMACHINE_FEATURES:append = " efi"\n'
1508 self.append_config(config)
1509 bitbake('core-image-minimal')
1510 self.remove_config(config)
1512 img = 'core-image-minimal'
1513 with NamedTemporaryFile("w", suffix=".wks") as wks:
1514 wks.writelines(['part /boot --source bootimg_efi --sourceparams="loader=uefi-kernel"\n'
1515 'part / --source rootfs --fstype=ext4 --align 1024 --use-uuid\n'\
1516 'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n'])
1518 cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1520 wksname = os.path.splitext(os.path.basename(wks.name))[0]
1521 out = glob(os.path.join(self.resultdir, "%s-*.direct" % wksname))
1522 self.assertEqual(1, len(out))
1524 # TODO this test could also work on aarch64
1525 @skipIfNotArch(['i586', 'i686', 'x86_64'])
1526 @OETestTag("runqemu")
1527 def test_efi_plugin_unified_kernel_image_qemu(self):
1528 """Test Unified Kernel Image feature in qemu without systemd in initramfs or rootfs"""
1530 # efi firmware must load systemd-boot, not grub
1531 EFI_PROVIDER = "systemd-boot"
1533 # image format must be wic, needs esp partition for firmware etc
1534 IMAGE_FSTYPES:pn-core-image-base:append = " wic"
1535 WKS_FILE = "test_efi_plugin.wks"
1537 # efi, uki and systemd features must be enabled
1538 MACHINE_FEATURES:append = " efi"
1539 IMAGE_CLASSES:append:pn-core-image-base = " uki"
1541 # uki embeds also an initrd, no systemd or udev
1542 INITRAMFS_IMAGE = "core-image-initramfs-boot"
1544 # runqemu must not load kernel separately, it's in the uki
1546 QB_DEFAULT_KERNEL = "none"
1548 # boot command line provided via uki, not via bootloader
1549 UKI_CMDLINE = "rootwait root=LABEL=root console=${KERNEL_CONSOLE}"
1552 self.append_config(config)
1553 bitbake('core-image-base ovmf')
1554 runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-base') or ""
1555 uki_filename = get_bb_var('UKI_FILENAME', 'core-image-base')
1556 self.remove_config(config)
1558 with runqemu('core-image-base', ssh=False,
1559 runqemuparams='%s nographic ovmf' % (runqemu_params), image_fstype='wic') as qemu:
1560 # Check that /boot has EFI boot*.efi (required for EFI)
1561 cmd = "ls /boot/EFI/BOOT/boot*.efi | wc -l"
1562 status, output = qemu.run_serial(cmd)
1563 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1564 self.assertEqual(output, '1')
1565 # Check that /boot has EFI/Linux/${UKI_FILENAME} (required for Unified Kernel Images auto detection)
1566 cmd = "ls /boot/EFI/Linux/%s | wc -l" % (uki_filename)
1567 status, output = qemu.run_serial(cmd)
1568 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1569 self.assertEqual(output, '1')
1570 # Check that /boot doesn't have loader/entries/boot.conf (Unified Kernel Images are auto detected by the bootloader)
1571 cmd = "ls /boot/loader/entries/boot.conf 2&>/dev/null | wc -l"
1572 status, output = qemu.run_serial(cmd)
1573 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1574 self.assertEqual(output, '0')
1576 @skipIfNotArch(['aarch64'])
1577 @OETestTag("runqemu")
1578 def test_efi_plugin_plain_systemd_boot_qemu_aarch64(self):
1579 """Test plain systemd-boot in qemu with systemd"""
1581 INIT_MANAGER = "systemd"
1582 EFI_PROVIDER = "systemd-boot"
1584 # image format must be wic, needs esp partition for firmware etc
1585 IMAGE_FSTYPES:pn-core-image-base:append = " wic"
1586 WKS_FILE = "test_efi_plugin_plain_systemd-boot.wks"
1588 INITRAMFS_IMAGE = "core-image-initramfs-boot"
1590 # to configure runqemu
1591 IMAGE_CLASSES += "qemuboot"
1592 # u-boot efi firmware
1593 QB_DEFAULT_BIOS = "u-boot.bin"
1594 # need to use virtio, scsi not supported by u-boot by default
1595 QB_DRIVE_TYPE = "/dev/vd"
1597 # disable kvm, breaks boot
1600 IMAGE_CLASSES:remove = 'testimage'
1602 self.append_config(config)
1603 bitbake('core-image-base u-boot')
1604 runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-base') or ""
1606 with runqemu('core-image-base', ssh=False,
1607 runqemuparams='%s nographic' % (runqemu_params), image_fstype='wic') as qemu:
1608 # Check that /boot has EFI boot*.efi (required for EFI)
1609 cmd = "ls /boot/EFI/BOOT/boot*.efi | wc -l"
1610 status, output = qemu.run_serial(cmd)
1611 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1612 self.assertEqual(output, '1')
1613 # Check that boot.conf exists
1614 cmd = "cat /boot/loader/entries/boot.conf"
1615 status, output = qemu.run_serial(cmd)
1616 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1617 self.remove_config(config)
1619 @skipIfNotArch(['i586', 'i686', 'x86_64'])
1620 @OETestTag("runqemu")
1621 def test_efi_plugin_plain_systemd_boot_qemu_x86(self):
1622 """Test plain systemd-boot to systemd in qemu"""
1624 INIT_MANAGER = "systemd"
1625 EFI_PROVIDER = "systemd-boot"
1627 # image format must be wic, needs esp partition for firmware etc
1628 IMAGE_FSTYPES:pn-core-image-base:append = " wic"
1629 WKS_FILE = "test_efi_plugin_plain_systemd-boot.wks"
1631 INITRAMFS_IMAGE = "core-image-initramfs-boot"
1633 self.append_config(config)
1634 bitbake('core-image-base ovmf')
1635 runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-base') or ""
1636 self.remove_config(config)
1638 with runqemu('core-image-base', ssh=False,
1639 runqemuparams='%s nographic ovmf' % (runqemu_params), image_fstype='wic') as qemu:
1640 # Check that /boot has EFI boot*.efi (required for EFI)
1641 cmd = "ls /boot/EFI/BOOT/boot*.efi | wc -l"
1642 status, output = qemu.run_serial(cmd)
1643 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1644 self.assertEqual(output, '1')
1645 # Check that boot.conf exists
1646 cmd = "cat /boot/loader/entries/boot.conf"
1647 status, output = qemu.run_serial(cmd)
1648 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1650 def test_fs_types(self):
1651 """Test filesystem types for empty and not empty partitions"""
1652 img = 'core-image-minimal'
1653 with NamedTemporaryFile("w", suffix=".wks") as wks:
1654 wks.writelines(['part ext2 --fstype ext2 --source rootfs\n',
1655 'part btrfs --fstype btrfs --source rootfs --size 40M\n',
1656 'part squash --fstype squashfs --source rootfs\n',
1657 'part swap --fstype swap --size 1M\n',
1658 'part emptyvfat --fstype vfat --size 1M\n',
1659 'part emptymsdos --fstype msdos --size 1M\n',
1660 'part emptyext2 --fstype ext2 --size 1M\n',
1661 'part emptybtrfs --fstype btrfs --size 150M\n'])
1663 cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1665 wksname = os.path.splitext(os.path.basename(wks.name))[0]
1666 out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1667 self.assertEqual(1, len(out))
1669 def test_kickstart_parser(self):
1670 """Test wks parser options"""
1671 with NamedTemporaryFile("w", suffix=".wks") as wks:
1672 wks.writelines(['part / --fstype ext3 --source rootfs --system-id 0xFF '\
1673 '--overhead-factor 1.2 --size 100k\n'])
1675 cmd = "wic create %s -e core-image-minimal -o %s" % (wks.name, self.resultdir)
1677 wksname = os.path.splitext(os.path.basename(wks.name))[0]
1678 out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1679 self.assertEqual(1, len(out))
1681 def test_image_bootpart_globbed(self):
1682 """Test globbed sources with image-bootpart plugin"""
1683 img = "core-image-minimal"
1684 cmd = "wic create sdimage-bootpart -e %s -o %s" % (img, self.resultdir)
1685 config = 'IMAGE_BOOT_FILES = "%s*"' % get_bb_var('KERNEL_IMAGETYPE', img)
1686 self.append_config(config)
1688 self.remove_config(config)
1689 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "sdimage-bootpart-*direct"))))
1691 def test_sparse_copy(self):
1692 """Test sparse_copy with FIEMAP and SEEK_HOLE filemap APIs"""
1693 libpath = os.path.join(self.td['COREBASE'], 'scripts', 'lib', 'wic')
1694 sys.path.insert(0, libpath)
1695 from filemap import FilemapFiemap, FilemapSeek, sparse_copy, ErrorNotSupp
1696 with NamedTemporaryFile("w", suffix=".wic-sparse") as sparse:
1697 src_name = sparse.name
1698 src_size = 1024 * 10
1699 sparse.truncate(src_size)
1700 # write one byte to the file
1701 with open(src_name, 'r+b') as sfile:
1702 sfile.seek(1024 * 4)
1703 sfile.write(b'\x00')
1704 dest = sparse.name + '.out'
1705 # copy src file to dest using different filemap APIs
1706 for api in (FilemapFiemap, FilemapSeek, None):
1707 if os.path.exists(dest):
1710 sparse_copy(sparse.name, dest, api=api)
1711 except ErrorNotSupp:
1712 continue # skip unsupported API
1713 dest_stat = os.stat(dest)
1714 self.assertEqual(dest_stat.st_size, src_size)
1715 # 8 blocks is 4K (physical sector size)
1716 self.assertEqual(dest_stat.st_blocks, 8)
1719 def test_mkfs_extraopts(self):
1720 """Test wks option --mkfs-extraopts for empty and not empty partitions"""
1721 img = 'core-image-minimal'
1722 with NamedTemporaryFile("w", suffix=".wks") as wks:
1724 ['part ext2 --fstype ext2 --source rootfs --mkfs-extraopts "-D -F -i 8192"\n',
1725 "part btrfs --fstype btrfs --source rootfs --size 40M --mkfs-extraopts='--quiet'\n",
1726 'part squash --fstype squashfs --source rootfs --mkfs-extraopts "-no-sparse -b 4096"\n',
1727 'part emptyvfat --fstype vfat --size 1M --mkfs-extraopts "-S 1024 -s 64"\n',
1728 'part emptymsdos --fstype msdos --size 1M --mkfs-extraopts "-S 1024 -s 64"\n',
1729 'part emptyext2 --fstype ext2 --size 1M --mkfs-extraopts "-D -F -i 8192"\n',
1730 'part emptybtrfs --fstype btrfs --size 100M --mkfs-extraopts "--mixed -K"\n'])
1732 cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1734 wksname = os.path.splitext(os.path.basename(wks.name))[0]
1735 out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1736 self.assertEqual(1, len(out))
1738 @skipIfNotArch(['i586', 'i686', 'x86_64'])
1739 @OETestTag("runqemu")
1740 def test_expand_mbr_image(self):
1741 """Test wic write --expand command for mbr image"""
1743 config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "directdisk.wks"\n'
1744 self.append_config(config)
1745 image = 'core-image-minimal'
1748 # get path to the image
1749 bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image)
1750 image_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], '%s.wic' % bb_vars['IMAGE_LINK_NAME'])
1752 self.remove_config(config)
1755 # expand image to 1G
1756 new_image_path = None
1757 with NamedTemporaryFile(mode='wb', suffix='.wic.exp',
1758 dir=bb_vars['DEPLOY_DIR_IMAGE'], delete=False) as sparse:
1759 sparse.truncate(1024 ** 3)
1760 new_image_path = sparse.name
1762 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1763 cmd = "wic write -n %s --expand 1:0 %s %s" % (sysroot, image_path, new_image_path)
1766 # check if partitions are expanded
1767 orig = runCmd("wic ls %s -n %s" % (image_path, sysroot))
1768 exp = runCmd("wic ls %s -n %s" % (new_image_path, sysroot))
1769 orig_sizes = [int(line.split()[3]) for line in orig.output.split('\n')[1:]]
1770 exp_sizes = [int(line.split()[3]) for line in exp.output.split('\n')[1:]]
1771 self.assertEqual(orig_sizes[0], exp_sizes[0]) # first partition is not resized
1772 self.assertTrue(orig_sizes[1] < exp_sizes[1], msg="Parition size wasn't enlarged (%s vs %s)" % (orig_sizes[1], exp_sizes[1]))
1774 # Check if all free space is partitioned
1775 result = runCmd("%s/usr/sbin/sfdisk -F %s" % (sysroot, new_image_path))
1776 self.assertIn("0 B, 0 bytes, 0 sectors", result.output)
1778 os.rename(image_path, image_path + '.bak')
1779 os.rename(new_image_path, image_path)
1781 runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-minimal') or ""
1782 with runqemu('core-image-minimal', ssh=False, runqemuparams='%s nographic' % (runqemu_params)) as qemu:
1784 status, output = qemu.run_serial('true')
1785 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1787 if os.path.exists(new_image_path):
1788 os.unlink(new_image_path)
1789 if os.path.exists(image_path + '.bak'):
1790 os.rename(image_path + '.bak', image_path)
1792 def test_gpt_partition_name(self):
1793 """Test --part-name argument to set partition name in GPT table"""
1794 config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "test_gpt_partition_name.wks"\n'
1795 self.append_config(config)
1796 image = 'core-image-minimal'
1798 self.remove_config(config)
1799 deploy_dir = get_bb_var('DEPLOY_DIR_IMAGE')
1800 bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image)
1801 image_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], '%s.wic' % bb_vars['IMAGE_LINK_NAME'])
1803 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1806 self.assertTrue(os.path.exists(image_path), "image file %s doesn't exist" % image_path)
1808 # Check the names of the three partitions
1809 # as listed in test_gpt_partition_name.wks
1810 result = runCmd("%s/usr/sbin/sfdisk --part-label %s 1" % (sysroot, image_path))
1811 self.assertEqual('boot-A', result.output)
1812 result = runCmd("%s/usr/sbin/sfdisk --part-label %s 2" % (sysroot, image_path))
1813 self.assertEqual('root-A', result.output)
1814 # When the --part-name is not defined, the partition name is equal to the --label
1815 result = runCmd("%s/usr/sbin/sfdisk --part-label %s 3" % (sysroot, image_path))
1816 self.assertEqual('ext-space', result.output)
1818 def test_empty_zeroize_plugin(self):
1819 img = 'core-image-minimal'
1820 expected_size = [ 1024*1024, # 1M
1823 # Check combination of sourceparams
1824 with NamedTemporaryFile("w", suffix=".wks") as wks:
1826 ['part empty --source empty --sourceparams="fill" --ondisk sda --fixed-size 1M\n',
1827 'part empty --source empty --sourceparams="size=512K" --ondisk sda --size 1M --align 1024\n',
1828 'part empty --source empty --sourceparams="size=2048k,bs=512K" --ondisk sda --size 4M --align 1024\n'
1831 cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1833 wksname = os.path.splitext(os.path.basename(wks.name))[0]
1834 wicout = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1835 # Skip the complete image and just look at the single partitions
1836 for idx, value in enumerate(wicout[1:]):
1837 self.logger.info(wicout[idx])
1838 # Check if partitions are actually zeroized
1839 with open(wicout[idx], mode="rb") as fd:
1840 ba = bytearray(fd.read())
1842 self.assertEqual(b, 0)
1843 self.assertEqual(expected_size[idx], os.path.getsize(wicout[idx]))
1845 # Check inconsistancy check between "fill" and "--size" parameter
1846 with NamedTemporaryFile("w", suffix=".wks") as wks:
1847 wks.writelines(['part empty --source empty --sourceparams="fill" --ondisk sda --size 1M\n'])
1849 cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1850 result = runCmd(cmd, ignore_status=True)
1851 self.assertIn("Source parameter 'fill' only works with the '--fixed-size' option, exiting.", result.output)
1852 self.assertNotEqual(0, result.status)
1854 class ModifyTests(WicTestCase):
1855 def test_wic_ls(self):
1856 """Test listing image content using 'wic ls'"""
1857 runCmd("wic create wictestdisk "
1858 "--image-name=core-image-minimal "
1859 "-D -o %s" % self.resultdir)
1860 images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
1861 self.assertEqual(1, len(images))
1863 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1866 result = runCmd("wic ls %s -n %s" % (images[0], sysroot))
1867 self.assertEqual(3, len(result.output.split('\n')))
1869 # list directory content of the first partition
1870 result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
1871 self.assertEqual(6, len(result.output.split('\n')))
1873 def test_wic_cp(self):
1874 """Test copy files and directories to the the wic image."""
1875 runCmd("wic create wictestdisk "
1876 "--image-name=core-image-minimal "
1877 "-D -o %s" % self.resultdir)
1878 images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
1879 self.assertEqual(1, len(images))
1881 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1883 # list directory content of the first partition
1884 result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
1885 self.assertEqual(6, len(result.output.split('\n')))
1887 with NamedTemporaryFile("w", suffix=".wic-cp") as testfile:
1888 testfile.write("test")
1890 # copy file to the partition
1891 runCmd("wic cp %s %s:1/ -n %s" % (testfile.name, images[0], sysroot))
1893 # check if file is there
1894 result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
1895 self.assertEqual(7, len(result.output.split('\n')))
1896 self.assertIn(os.path.basename(testfile.name), result.output)
1899 testdir = os.path.join(self.resultdir, 'wic-test-cp-dir')
1900 testsubdir = os.path.join(testdir, 'subdir')
1901 os.makedirs(os.path.join(testsubdir))
1902 copy(testfile.name, testdir)
1904 # copy directory to the partition
1905 runCmd("wic cp %s %s:1/ -n %s" % (testdir, images[0], sysroot))
1907 # check if directory is there
1908 result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
1909 self.assertEqual(8, len(result.output.split('\n')))
1910 self.assertIn(os.path.basename(testdir), result.output)
1912 # copy the file from the partition and check if it success
1913 dest = '%s-cp' % testfile.name
1914 runCmd("wic cp %s:1/%s %s -n %s" % (images[0],
1915 os.path.basename(testfile.name), dest, sysroot))
1916 self.assertTrue(os.path.exists(dest), msg="File %s wasn't generated as expected" % dest)
1919 def test_wic_rm(self):
1920 """Test removing files and directories from the the wic image."""
1921 runCmd("wic create mkefidisk "
1922 "--image-name=core-image-minimal "
1923 "-D -o %s" % self.resultdir)
1924 images = glob(os.path.join(self.resultdir, "mkefidisk-*.direct"))
1925 self.assertEqual(1, len(images))
1927 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1928 # Not bulletproof but hopefully sufficient
1929 kerneltype = get_bb_var('KERNEL_IMAGETYPE', 'virtual/kernel')
1931 # list directory content of the first partition
1932 result = runCmd("wic ls %s:1 -n %s" % (images[0], sysroot))
1933 self.assertIn('\n%s ' % kerneltype.upper(), result.output)
1934 self.assertIn('\nEFI <DIR> ', result.output)
1936 # remove file. EFI partitions are case-insensitive so exercise that too
1937 runCmd("wic rm %s:1/%s -n %s" % (images[0], kerneltype.lower(), sysroot))
1940 runCmd("wic rm %s:1/efi -n %s" % (images[0], sysroot))
1942 # check if they're removed
1943 result = runCmd("wic ls %s:1 -n %s" % (images[0], sysroot))
1944 self.assertNotIn('\n%s ' % kerneltype.upper(), result.output)
1945 self.assertNotIn('\nEFI <DIR> ', result.output)
1947 def test_wic_ls_ext(self):
1948 """Test listing content of the ext partition using 'wic ls'"""
1949 runCmd("wic create wictestdisk "
1950 "--image-name=core-image-minimal "
1951 "-D -o %s" % self.resultdir)
1952 images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
1953 self.assertEqual(1, len(images))
1955 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1957 # list directory content of the second ext4 partition
1958 result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot))
1959 self.assertTrue(set(['bin', 'home', 'proc', 'usr', 'var', 'dev', 'lib', 'sbin']).issubset(
1960 set(line.split()[-1] for line in result.output.split('\n') if line)), msg="Expected directories not present %s" % result.output)
1962 def test_wic_cp_ext(self):
1963 """Test copy files and directories to the ext partition."""
1964 runCmd("wic create wictestdisk "
1965 "--image-name=core-image-minimal "
1966 "-D -o %s" % self.resultdir)
1967 images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
1968 self.assertEqual(1, len(images))
1970 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1972 # list directory content of the ext4 partition
1973 result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot))
1974 dirs = set(line.split()[-1] for line in result.output.split('\n') if line)
1975 self.assertTrue(set(['bin', 'home', 'proc', 'usr', 'var', 'dev', 'lib', 'sbin']).issubset(dirs), msg="Expected directories not present %s" % dirs)
1977 with NamedTemporaryFile("w", suffix=".wic-cp") as testfile:
1978 testfile.write("test")
1980 # copy file to the partition
1981 runCmd("wic cp %s %s:2/ -n %s" % (testfile.name, images[0], sysroot))
1983 # check if file is there
1984 result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot))
1985 newdirs = set(line.split()[-1] for line in result.output.split('\n') if line)
1986 self.assertEqual(newdirs.difference(dirs), set([os.path.basename(testfile.name)]))
1988 # check if the file to copy is in the partition
1989 result = runCmd("wic ls %s:2/etc/ -n %s" % (images[0], sysroot))
1990 self.assertIn('fstab', [line.split()[-1] for line in result.output.split('\n') if line])
1992 # copy file from the partition, replace the temporary file content with it and
1993 # check for the file size to validate the copy
1994 runCmd("wic cp %s:2/etc/fstab %s -n %s" % (images[0], testfile.name, sysroot))
1995 self.assertTrue(os.stat(testfile.name).st_size > 0, msg="Filesize not as expected %s" % os.stat(testfile.name).st_size)
1998 def test_wic_rm_ext(self):
1999 """Test removing files from the ext partition."""
2000 runCmd("wic create mkefidisk "
2001 "--image-name=core-image-minimal "
2002 "-D -o %s" % self.resultdir)
2003 images = glob(os.path.join(self.resultdir, "mkefidisk-*.direct"))
2004 self.assertEqual(1, len(images))
2006 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
2008 # list directory content of the /etc directory on ext4 partition
2009 result = runCmd("wic ls %s:2/etc/ -n %s" % (images[0], sysroot))
2010 self.assertIn('fstab', [line.split()[-1] for line in result.output.split('\n') if line])
2013 runCmd("wic rm %s:2/etc/fstab -n %s" % (images[0], sysroot))
2015 # check if it's removed
2016 result = runCmd("wic ls %s:2/etc/ -n %s" % (images[0], sysroot))
2017 self.assertNotIn('fstab', [line.split()[-1] for line in result.output.split('\n') if line])
2019 # remove non-empty directory
2020 runCmd("wic rm -r %s:2/etc/ -n %s" % (images[0], sysroot))
2022 # check if it's removed
2023 result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot))
2024 self.assertNotIn('etc', [line.split()[-1] for line in result.output.split('\n') if line])