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 11import os 12import sys 13import unittest 14import hashlib 15import subprocess 16 17from glob import glob 18from shutil import rmtree, copy 19from tempfile import NamedTemporaryFile 20from tempfile import TemporaryDirectory 21 22from oeqa.selftest.case import OESelftestTestCase 23from oeqa.core.decorator import OETestTag 24from oeqa.core.decorator.data import skipIfNotArch 25from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars, runqemu 26 27 28def 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 46def 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 53class 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 89class 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 150class 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(""" 447part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path usr 448part /usr --source rootfs --ondisk mmcblk0 --fstype=ext4 --rootfs-dir %s/usr 449part /etc --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path bin/ --rootfs-dir %s/usr 450part /mnt --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path bin/whoami --rootfs-dir %s/usr""" 451 % (rootfs_dir, rootfs_dir, rootfs_dir)) 452 runCmd("wic create %s -e core-image-minimal -o %s" \ 453 % (wks_file, self.resultdir)) 454 455 os.remove(wks_file) 456 wicout = glob(os.path.join(self.resultdir, "%s-*direct" % 'temp')) 457 self.assertEqual(1, len(wicout)) 458 459 wicimg = wicout[0] 460 461 # verify partition size with wic 462 res = runCmd("parted -m %s unit b p" % wicimg, stderr=subprocess.PIPE) 463 464 # parse parted output which looks like this: 465 # BYT;\n 466 # /var/tmp/wic/build/tmpfwvjjkf_-201611101222-hda.direct:200MiB:file:512:512:msdos::;\n 467 # 1:0.00MiB:200MiB:200MiB:ext4::;\n 468 partlns = res.output.splitlines()[2:] 469 470 self.assertEqual(4, len(partlns)) 471 472 for part in [1, 2, 3, 4]: 473 part_file = os.path.join(self.resultdir, "selftest_img.part%d" % part) 474 partln = partlns[part-1].split(":") 475 self.assertEqual(7, len(partln)) 476 start = int(partln[1].rstrip("B")) / 512 477 length = int(partln[3].rstrip("B")) / 512 478 runCmd("dd if=%s of=%s skip=%d count=%d" % 479 (wicimg, part_file, start, length)) 480 481 # Test partition 1, should contain the normal root directories, except 482 # /usr. 483 res = runCmd("debugfs -R 'ls -p' %s" % \ 484 os.path.join(self.resultdir, "selftest_img.part1"), stderr=subprocess.PIPE) 485 files = extract_files(res.output) 486 self.assertIn("etc", files) 487 self.assertNotIn("usr", files) 488 489 # Partition 2, should contain common directories for /usr, not root 490 # directories. 491 res = runCmd("debugfs -R 'ls -p' %s" % \ 492 os.path.join(self.resultdir, "selftest_img.part2"), stderr=subprocess.PIPE) 493 files = extract_files(res.output) 494 self.assertNotIn("etc", files) 495 self.assertNotIn("usr", files) 496 self.assertIn("share", files) 497 498 # Partition 3, should contain the same as partition 2, including the bin 499 # directory, but not the files inside it. 500 res = runCmd("debugfs -R 'ls -p' %s" % \ 501 os.path.join(self.resultdir, "selftest_img.part3"), stderr=subprocess.PIPE) 502 files = extract_files(res.output) 503 self.assertNotIn("etc", files) 504 self.assertNotIn("usr", files) 505 self.assertIn("share", files) 506 self.assertIn("bin", files) 507 res = runCmd("debugfs -R 'ls -p bin' %s" % \ 508 os.path.join(self.resultdir, "selftest_img.part3"), stderr=subprocess.PIPE) 509 files = extract_files(res.output) 510 self.assertIn(".", files) 511 self.assertIn("..", files) 512 self.assertEqual(2, len(files)) 513 514 # Partition 4, should contain the same as partition 2, including the bin 515 # directory, but not whoami (a symlink to busybox.nosuid) inside it. 516 res = runCmd("debugfs -R 'ls -p' %s" % \ 517 os.path.join(self.resultdir, "selftest_img.part4"), stderr=subprocess.PIPE) 518 files = extract_files(res.output) 519 self.assertNotIn("etc", files) 520 self.assertNotIn("usr", files) 521 self.assertIn("share", files) 522 self.assertIn("bin", files) 523 res = runCmd("debugfs -R 'ls -p bin' %s" % \ 524 os.path.join(self.resultdir, "selftest_img.part4"), stderr=subprocess.PIPE) 525 files = extract_files(res.output) 526 self.assertIn(".", files) 527 self.assertIn("..", files) 528 self.assertIn("who", files) 529 self.assertNotIn("whoami", files) 530 531 for part in [1, 2, 3, 4]: 532 part_file = os.path.join(self.resultdir, "selftest_img.part%d" % part) 533 os.remove(part_file) 534 535 finally: 536 os.environ['PATH'] = oldpath 537 538 def test_exclude_path_with_extra_space(self): 539 """Test having --exclude-path with IMAGE_ROOTFS_EXTRA_SPACE. [Yocto #15555]""" 540 541 with NamedTemporaryFile("w", suffix=".wks") as wks: 542 wks.writelines( 543 ['bootloader --ptable gpt\n', 544 'part /boot --size=100M --active --fstype=ext4 --label boot\n', 545 'part / --source rootfs --fstype=ext4 --label root --exclude-path boot/\n']) 546 wks.flush() 547 config = 'IMAGE_ROOTFS_EXTRA_SPACE = "500000"\n'\ 548 'DEPENDS:pn-core-image-minimal += "wic-tools"\n'\ 549 'IMAGE_FSTYPES += "wic ext4"\n'\ 550 'WKS_FILE = "%s"\n' % wks.name 551 self.append_config(config) 552 bitbake('core-image-minimal') 553 554 """ 555 the output of "wic ls <image>.wic" will look something like: 556 Num Start End Size Fstype 557 1 17408 136332287 136314880 ext4 558 2 136332288 171464703 35132416 ext4 559 we are looking for the size of partition 2 560 i.e. in this case the number 35,132,416 561 without the fix the size will be around 85,403,648 562 with the fix the size should be around 799,960,064 563 """ 564 bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'MACHINE'], 'core-image-minimal') 565 deploy_dir = bb_vars['DEPLOY_DIR_IMAGE'] 566 machine = bb_vars['MACHINE'] 567 nativesysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools') 568 wicout = glob(os.path.join(deploy_dir, "core-image-minimal-%s.rootfs-*.wic" % machine))[0] 569 size_of_root_partition = int(runCmd("wic ls %s --native-sysroot %s" % (wicout, nativesysroot)).output.split('\n')[2].split()[3]) 570 self.assertGreater(size_of_root_partition, 500000000) 571 572 def test_include_path(self): 573 """Test --include-path wks option.""" 574 575 oldpath = os.environ['PATH'] 576 os.environ['PATH'] = get_bb_var("PATH", "wic-tools") 577 578 try: 579 include_path = os.path.join(self.resultdir, 'test-include') 580 os.makedirs(include_path) 581 with open(os.path.join(include_path, 'test-file'), 'w') as t: 582 t.write("test\n") 583 wks_file = os.path.join(include_path, 'temp.wks') 584 with open(wks_file, 'w') as wks: 585 rootfs_dir = get_bb_var('IMAGE_ROOTFS', 'core-image-minimal') 586 wks.write(""" 587part /part1 --source rootfs --ondisk mmcblk0 --fstype=ext4 588part /part2 --source rootfs --ondisk mmcblk0 --fstype=ext4 --include-path %s""" 589 % (include_path)) 590 runCmd("wic create %s -e core-image-minimal -o %s" \ 591 % (wks_file, self.resultdir)) 592 593 part1 = glob(os.path.join(self.resultdir, 'temp-*.direct.p1'))[0] 594 part2 = glob(os.path.join(self.resultdir, 'temp-*.direct.p2'))[0] 595 596 # Test partition 1, should not contain 'test-file' 597 res = runCmd("debugfs -R 'ls -p' %s" % (part1), stderr=subprocess.PIPE) 598 files = extract_files(res.output) 599 self.assertNotIn('test-file', files) 600 self.assertEqual(True, files_own_by_root(res.output)) 601 602 # Test partition 2, should contain 'test-file' 603 res = runCmd("debugfs -R 'ls -p' %s" % (part2), stderr=subprocess.PIPE) 604 files = extract_files(res.output) 605 self.assertIn('test-file', files) 606 self.assertEqual(True, files_own_by_root(res.output)) 607 608 finally: 609 os.environ['PATH'] = oldpath 610 611 def test_include_path_embeded(self): 612 """Test --include-path wks option.""" 613 614 oldpath = os.environ['PATH'] 615 os.environ['PATH'] = get_bb_var("PATH", "wic-tools") 616 617 try: 618 include_path = os.path.join(self.resultdir, 'test-include') 619 os.makedirs(include_path) 620 with open(os.path.join(include_path, 'test-file'), 'w') as t: 621 t.write("test\n") 622 wks_file = os.path.join(include_path, 'temp.wks') 623 with open(wks_file, 'w') as wks: 624 wks.write(""" 625part / --source rootfs --fstype=ext4 --include-path %s --include-path core-image-minimal-mtdutils export/""" 626 % (include_path)) 627 runCmd("wic create %s -e core-image-minimal -o %s" \ 628 % (wks_file, self.resultdir)) 629 630 part1 = glob(os.path.join(self.resultdir, 'temp-*.direct.p1'))[0] 631 632 res = runCmd("debugfs -R 'ls -p' %s" % (part1), stderr=subprocess.PIPE) 633 files = extract_files(res.output) 634 self.assertIn('test-file', files) 635 self.assertEqual(True, files_own_by_root(res.output)) 636 637 res = runCmd("debugfs -R 'ls -p /export/etc/' %s" % (part1), stderr=subprocess.PIPE) 638 files = extract_files(res.output) 639 self.assertIn('passwd', files) 640 self.assertEqual(True, files_own_by_root(res.output)) 641 642 finally: 643 os.environ['PATH'] = oldpath 644 645 def test_include_path_errors(self): 646 """Test --include-path wks option error handling.""" 647 wks_file = 'temp.wks' 648 649 # Absolute argument. 650 with open(wks_file, 'w') as wks: 651 wks.write("part / --source rootfs --fstype=ext4 --include-path core-image-minimal-mtdutils /export") 652 self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \ 653 % (wks_file, self.resultdir), ignore_status=True).status) 654 os.remove(wks_file) 655 656 # Argument pointing to parent directory. 657 with open(wks_file, 'w') as wks: 658 wks.write("part / --source rootfs --fstype=ext4 --include-path core-image-minimal-mtdutils ././..") 659 self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \ 660 % (wks_file, self.resultdir), ignore_status=True).status) 661 os.remove(wks_file) 662 663 # 3 Argument pointing to parent directory. 664 with open(wks_file, 'w') as wks: 665 wks.write("part / --source rootfs --fstype=ext4 --include-path core-image-minimal-mtdutils export/ dummy") 666 self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \ 667 % (wks_file, self.resultdir), ignore_status=True).status) 668 os.remove(wks_file) 669 670 def test_exclude_path_errors(self): 671 """Test --exclude-path wks option error handling.""" 672 wks_file = 'temp.wks' 673 674 # Absolute argument. 675 with open(wks_file, 'w') as wks: 676 wks.write("part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path /usr") 677 self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \ 678 % (wks_file, self.resultdir), ignore_status=True).status) 679 os.remove(wks_file) 680 681 # Argument pointing to parent directory. 682 with open(wks_file, 'w') as wks: 683 wks.write("part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path ././..") 684 self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \ 685 % (wks_file, self.resultdir), ignore_status=True).status) 686 os.remove(wks_file) 687 688 def test_permissions(self): 689 """Test permissions are respected""" 690 691 # prepare wicenv and rootfs 692 bitbake('core-image-minimal core-image-minimal-mtdutils -c do_rootfs_wicenv') 693 694 oldpath = os.environ['PATH'] 695 os.environ['PATH'] = get_bb_var("PATH", "wic-tools") 696 697 t_normal = """ 698part / --source rootfs --fstype=ext4 699""" 700 t_exclude = """ 701part / --source rootfs --fstype=ext4 --exclude-path=home 702""" 703 t_multi = """ 704part / --source rootfs --ondisk sda --fstype=ext4 705part /export --source rootfs --rootfs=core-image-minimal-mtdutils --fstype=ext4 706""" 707 t_change = """ 708part / --source rootfs --ondisk sda --fstype=ext4 --exclude-path=etc/ 709part /etc --source rootfs --fstype=ext4 --change-directory=etc 710""" 711 tests = [t_normal, t_exclude, t_multi, t_change] 712 713 try: 714 for test in tests: 715 include_path = os.path.join(self.resultdir, 'test-include') 716 os.makedirs(include_path) 717 wks_file = os.path.join(include_path, 'temp.wks') 718 with open(wks_file, 'w') as wks: 719 wks.write(test) 720 runCmd("wic create %s -e core-image-minimal -o %s" \ 721 % (wks_file, self.resultdir)) 722 723 for part in glob(os.path.join(self.resultdir, 'temp-*.direct.p*')): 724 res = runCmd("debugfs -R 'ls -p' %s" % (part), stderr=subprocess.PIPE) 725 self.assertEqual(True, files_own_by_root(res.output)) 726 727 config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "%s"\n' % wks_file 728 self.append_config(config) 729 bitbake('core-image-minimal') 730 tmpdir = os.path.join(get_bb_var('WORKDIR', 'core-image-minimal'),'build-wic') 731 732 # check each partition for permission 733 for part in glob(os.path.join(tmpdir, 'temp-*.direct.p*')): 734 res = runCmd("debugfs -R 'ls -p' %s" % (part), stderr=subprocess.PIPE) 735 self.assertTrue(files_own_by_root(res.output) 736 ,msg='Files permission incorrect using wks set "%s"' % test) 737 738 # clean config and result directory for next cases 739 self.remove_config(config) 740 rmtree(self.resultdir, ignore_errors=True) 741 742 finally: 743 os.environ['PATH'] = oldpath 744 745 def test_change_directory(self): 746 """Test --change-directory wks option.""" 747 748 oldpath = os.environ['PATH'] 749 os.environ['PATH'] = get_bb_var("PATH", "wic-tools") 750 751 try: 752 include_path = os.path.join(self.resultdir, 'test-include') 753 os.makedirs(include_path) 754 wks_file = os.path.join(include_path, 'temp.wks') 755 with open(wks_file, 'w') as wks: 756 wks.write("part /etc --source rootfs --fstype=ext4 --change-directory=etc") 757 runCmd("wic create %s -e core-image-minimal -o %s" \ 758 % (wks_file, self.resultdir)) 759 760 part1 = glob(os.path.join(self.resultdir, 'temp-*.direct.p1'))[0] 761 762 res = runCmd("debugfs -R 'ls -p' %s" % (part1), stderr=subprocess.PIPE) 763 files = extract_files(res.output) 764 self.assertIn('passwd', files) 765 766 finally: 767 os.environ['PATH'] = oldpath 768 769 def test_change_directory_errors(self): 770 """Test --change-directory wks option error handling.""" 771 wks_file = 'temp.wks' 772 773 # Absolute argument. 774 with open(wks_file, 'w') as wks: 775 wks.write("part / --source rootfs --fstype=ext4 --change-directory /usr") 776 self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \ 777 % (wks_file, self.resultdir), ignore_status=True).status) 778 os.remove(wks_file) 779 780 # Argument pointing to parent directory. 781 with open(wks_file, 'w') as wks: 782 wks.write("part / --source rootfs --fstype=ext4 --change-directory ././..") 783 self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \ 784 % (wks_file, self.resultdir), ignore_status=True).status) 785 os.remove(wks_file) 786 787 def test_no_fstab_update(self): 788 """Test --no-fstab-update wks option.""" 789 790 oldpath = os.environ['PATH'] 791 os.environ['PATH'] = get_bb_var("PATH", "wic-tools") 792 793 # Get stock fstab from base-files recipe 794 bitbake('base-files -c do_install') 795 bf_fstab = os.path.join(get_bb_var('D', 'base-files'), 'etc', 'fstab') 796 self.assertEqual(True, os.path.exists(bf_fstab)) 797 bf_fstab_md5sum = runCmd('md5sum %s ' % bf_fstab).output.split(" ")[0] 798 799 try: 800 no_fstab_update_path = os.path.join(self.resultdir, 'test-no-fstab-update') 801 os.makedirs(no_fstab_update_path) 802 wks_file = os.path.join(no_fstab_update_path, 'temp.wks') 803 with open(wks_file, 'w') as wks: 804 wks.writelines(['part / --source rootfs --fstype=ext4 --label rootfs\n', 805 'part /mnt/p2 --source rootfs --rootfs-dir=core-image-minimal ', 806 '--fstype=ext4 --label p2 --no-fstab-update\n']) 807 runCmd("wic create %s -e core-image-minimal -o %s" \ 808 % (wks_file, self.resultdir)) 809 810 part_fstab_md5sum = [] 811 for i in range(1, 3): 812 part = glob(os.path.join(self.resultdir, 'temp-*.direct.p') + str(i))[0] 813 part_fstab = runCmd("debugfs -R 'cat etc/fstab' %s" % (part), stderr=subprocess.PIPE) 814 part_fstab_md5sum.append(hashlib.md5((part_fstab.output + "\n\n").encode('utf-8')).hexdigest()) 815 816 # '/etc/fstab' in partition 2 should contain the same stock fstab file 817 # as the one installed by the base-file recipe. 818 self.assertEqual(bf_fstab_md5sum, part_fstab_md5sum[1]) 819 820 # '/etc/fstab' in partition 1 should contain an updated fstab file. 821 self.assertNotEqual(bf_fstab_md5sum, part_fstab_md5sum[0]) 822 823 finally: 824 os.environ['PATH'] = oldpath 825 826 def test_no_fstab_update_errors(self): 827 """Test --no-fstab-update wks option error handling.""" 828 wks_file = 'temp.wks' 829 830 # Absolute argument. 831 with open(wks_file, 'w') as wks: 832 wks.write("part / --source rootfs --fstype=ext4 --no-fstab-update /etc") 833 self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \ 834 % (wks_file, self.resultdir), ignore_status=True).status) 835 os.remove(wks_file) 836 837 # Argument pointing to parent directory. 838 with open(wks_file, 'w') as wks: 839 wks.write("part / --source rootfs --fstype=ext4 --no-fstab-update ././..") 840 self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \ 841 % (wks_file, self.resultdir), ignore_status=True).status) 842 os.remove(wks_file) 843 844 def test_extra_space(self): 845 """Test --extra-space wks option.""" 846 extraspace = 1024**3 847 runCmd("wic create wictestdisk " 848 "--image-name core-image-minimal " 849 "--extra-space %i -o %s" % (extraspace ,self.resultdir)) 850 wicout = glob(os.path.join(self.resultdir, "wictestdisk-*.direct")) 851 self.assertEqual(1, len(wicout)) 852 size = os.path.getsize(wicout[0]) 853 self.assertTrue(size > extraspace, msg="Extra space not present (%s vs %s)" % (size, extraspace)) 854 855 def test_no_table(self): 856 """Test --no-table wks option.""" 857 wks_file = 'temp.wks' 858 859 # Absolute argument. 860 with open(wks_file, 'w') as wks: 861 wks.write("part testspace --no-table --fixed-size 16k --offset 4080k") 862 runCmd("wic create %s --image-name core-image-minimal -o %s" % (wks_file, self.resultdir)) 863 864 wicout = glob(os.path.join(self.resultdir, "*.*")) 865 866 self.assertEqual(1, len(wicout)) 867 size = os.path.getsize(wicout[0]) 868 self.assertEqual(size, 4 * 1024 * 1024) 869 870 os.remove(wks_file) 871 872 def test_partition_hidden_attributes(self): 873 """Test --hidden wks option.""" 874 wks_file = 'temp.wks' 875 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools') 876 try: 877 with open(wks_file, 'w') as wks: 878 wks.write(""" 879part / --source rootfs --fstype=ext4 880part / --source rootfs --fstype=ext4 --hidden 881bootloader --ptable gpt""") 882 883 runCmd("wic create %s -e core-image-minimal -o %s" \ 884 % (wks_file, self.resultdir)) 885 wicout = os.path.join(self.resultdir, "*.direct") 886 887 result = runCmd("%s/usr/sbin/sfdisk --part-attrs %s 1" % (sysroot, wicout)) 888 self.assertEqual('', result.output) 889 result = runCmd("%s/usr/sbin/sfdisk --part-attrs %s 2" % (sysroot, wicout)) 890 self.assertEqual('RequiredPartition', result.output) 891 892 finally: 893 os.remove(wks_file) 894 895 def test_wic_sector_size(self): 896 """Test generation image sector size""" 897 898 oldpath = os.environ['PATH'] 899 os.environ['PATH'] = get_bb_var("PATH", "wic-tools") 900 901 try: 902 # Add WIC_SECTOR_SIZE into config 903 config = 'WIC_SECTOR_SIZE = "4096"\n'\ 904 'WICVARS:append = " WIC_SECTOR_SIZE"\n' 905 self.append_config(config) 906 bitbake('core-image-minimal') 907 908 # Check WIC_SECTOR_SIZE apply to bitbake variable 909 wic_sector_size_str = get_bb_var('WIC_SECTOR_SIZE', 'core-image-minimal') 910 wic_sector_size = int(wic_sector_size_str) 911 self.assertEqual(4096, wic_sector_size) 912 913 self.logger.info("Test wic_sector_size: %d \n" % wic_sector_size) 914 915 with NamedTemporaryFile("w", suffix=".wks") as wks: 916 wks.writelines( 917 ['bootloader --ptable gpt\n', 918 'part --fstype ext4 --source rootfs --label rofs-a --mkfs-extraopts "-b 4096"\n', 919 'part --fstype ext4 --source rootfs --use-uuid --mkfs-extraopts "-b 4096"\n']) 920 wks.flush() 921 cmd = "wic create %s -e core-image-minimal -o %s" % (wks.name, self.resultdir) 922 runCmd(cmd) 923 wksname = os.path.splitext(os.path.basename(wks.name))[0] 924 images = glob(os.path.join(self.resultdir, "%s-*direct" % wksname)) 925 self.assertEqual(1, len(images)) 926 927 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools') 928 # list partitions 929 result = runCmd("wic ls %s -n %s" % (images[0], sysroot)) 930 self.assertEqual(3, len(result.output.split('\n'))) 931 932 # verify partition size with wic 933 res = runCmd("export PARTED_SECTOR_SIZE=%d; parted -m %s unit b p" % (wic_sector_size, images[0]), 934 stderr=subprocess.PIPE) 935 936 # parse parted output which looks like this: 937 # BYT;\n 938 # /var/tmp/wic/build/tmpgjzzefdd-202410281021-sda.direct:78569472B:file:4096:4096:gpt::;\n 939 # 1:139264B:39284735B:39145472B:ext4:rofs-a:;\n 940 # 2:39284736B:78430207B:39145472B:ext4:primary:;\n 941 disk_info = res.output.splitlines()[1] 942 # Check sector sizes 943 sector_size_logical = int(disk_info.split(":")[3]) 944 sector_size_physical = int(disk_info.split(":")[4]) 945 self.assertEqual(wic_sector_size, sector_size_logical, "Logical sector size is not %d." % wic_sector_size) 946 self.assertEqual(wic_sector_size, sector_size_physical, "Physical sector size is not %d." % wic_sector_size) 947 948 finally: 949 os.environ['PATH'] = oldpath 950 951class Wic2(WicTestCase): 952 953 def test_bmap_short(self): 954 """Test generation of .bmap file -m option""" 955 cmd = "wic create wictestdisk -e core-image-minimal -m -o %s" % self.resultdir 956 runCmd(cmd) 957 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct")))) 958 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct.bmap")))) 959 960 def test_bmap_long(self): 961 """Test generation of .bmap file --bmap option""" 962 cmd = "wic create wictestdisk -e core-image-minimal --bmap -o %s" % self.resultdir 963 runCmd(cmd) 964 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct")))) 965 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct.bmap")))) 966 967 def test_image_env(self): 968 """Test generation of <image>.env files.""" 969 image = 'core-image-minimal' 970 imgdatadir = self._get_image_env_path(image) 971 972 bb_vars = get_bb_vars(['IMAGE_BASENAME', 'WICVARS'], image) 973 basename = bb_vars['IMAGE_BASENAME'] 974 self.assertEqual(basename, image) 975 path = os.path.join(imgdatadir, basename) + '.env' 976 self.assertTrue(os.path.isfile(path), msg="File %s wasn't generated as expected" % path) 977 978 wicvars = set(bb_vars['WICVARS'].split()) 979 # filter out optional variables 980 wicvars = wicvars.difference(('DEPLOY_DIR_IMAGE', 'IMAGE_BOOT_FILES', 981 'INITRD', 'INITRD_LIVE', 'ISODIR','INITRAMFS_IMAGE', 982 'INITRAMFS_IMAGE_BUNDLE', 'INITRAMFS_LINK_NAME', 983 'APPEND', 'IMAGE_EFI_BOOT_FILES')) 984 with open(path) as envfile: 985 content = dict(line.split("=", 1) for line in envfile) 986 # test if variables used by wic present in the .env file 987 for var in wicvars: 988 self.assertTrue(var in content, "%s is not in .env file" % var) 989 self.assertTrue(content[var], "%s doesn't have a value (%s)" % (var, content[var])) 990 991 def test_image_vars_dir_short(self): 992 """Test image vars directory selection -v option""" 993 image = 'core-image-minimal' 994 imgenvdir = self._get_image_env_path(image) 995 native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools") 996 997 runCmd("wic create wictestdisk " 998 "--image-name=%s -v %s -n %s -o %s" 999 % (image, imgenvdir, native_sysroot, 1000 self.resultdir)) 1001 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct")))) 1002 1003 def test_image_vars_dir_long(self): 1004 """Test image vars directory selection --vars option""" 1005 image = 'core-image-minimal' 1006 imgenvdir = self._get_image_env_path(image) 1007 native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools") 1008 1009 runCmd("wic create wictestdisk " 1010 "--image-name=%s " 1011 "--vars %s " 1012 "--native-sysroot %s " 1013 "--outdir %s" 1014 % (image, imgenvdir, native_sysroot, 1015 self.resultdir)) 1016 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct")))) 1017 1018 # TODO this test could also work on aarch64 1019 @skipIfNotArch(['i586', 'i686', 'x86_64']) 1020 def test_wic_image_type(self): 1021 """Test building wic images by bitbake""" 1022 config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "wic-image-minimal"\n'\ 1023 'MACHINE_FEATURES:append = " efi"\n' 1024 self.append_config(config) 1025 image = 'wic-image-minimal' 1026 bitbake(image) 1027 self.remove_config(config) 1028 1029 bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image) 1030 prefix = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], '%s.' % bb_vars['IMAGE_LINK_NAME']) 1031 1032 # check if we have result image and manifests symlinks 1033 # pointing to existing files 1034 for suffix in ('wic', 'manifest'): 1035 path = prefix + suffix 1036 self.assertTrue(os.path.islink(path), msg="Link %s wasn't generated as expected" % path) 1037 self.assertTrue(os.path.isfile(os.path.realpath(path)), msg="File linked to by %s wasn't generated as expected" % path) 1038 1039 # TODO this should work on aarch64 1040 @skipIfNotArch(['i586', 'i686', 'x86_64']) 1041 @OETestTag("runqemu") 1042 def test_qemu(self): 1043 """Test wic-image-minimal under qemu""" 1044 config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "wic-image-minimal"\n'\ 1045 'MACHINE_FEATURES:append = " efi"\n' 1046 self.append_config(config) 1047 bitbake('wic-image-minimal') 1048 self.remove_config(config) 1049 1050 runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'wic-image-minimal') or "" 1051 with runqemu('wic-image-minimal', ssh=False, runqemuparams='%s nographic' % (runqemu_params)) as qemu: 1052 cmd = "mount | grep '^/dev/' | cut -f1,3 -d ' ' | egrep -c -e '/dev/sda1 /boot' " \ 1053 "-e '/dev/root /|/dev/sda2 /' -e '/dev/sda3 /media' -e '/dev/sda4 /mnt'" 1054 status, output = qemu.run_serial(cmd) 1055 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) 1056 self.assertEqual(output, '4') 1057 cmd = "grep UUID=2c71ef06-a81d-4735-9d3a-379b69c6bdba /etc/fstab" 1058 status, output = qemu.run_serial(cmd) 1059 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) 1060 self.assertEqual(output, 'UUID=2c71ef06-a81d-4735-9d3a-379b69c6bdba\t/media\text4\tdefaults\t0\t0') 1061 1062 @skipIfNotArch(['i586', 'i686', 'x86_64']) 1063 @OETestTag("runqemu") 1064 def test_qemu_efi(self): 1065 """Test core-image-minimal efi image under qemu""" 1066 config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "mkefidisk.wks"\n' 1067 self.append_config(config) 1068 bitbake('core-image-minimal ovmf') 1069 self.remove_config(config) 1070 1071 runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-minimal') or "" 1072 with runqemu('core-image-minimal', ssh=False, 1073 runqemuparams='%s nographic ovmf' % (runqemu_params), image_fstype='wic') as qemu: 1074 cmd = "grep sda. /proc/partitions |wc -l" 1075 status, output = qemu.run_serial(cmd) 1076 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) 1077 self.assertEqual(output, '3') 1078 1079 @staticmethod 1080 def _make_fixed_size_wks(size): 1081 """ 1082 Create a wks of an image with a single partition. Size of the partition is set 1083 using --fixed-size flag. Returns a tuple: (path to wks file, wks image name) 1084 """ 1085 with NamedTemporaryFile("w", suffix=".wks", delete=False) as tempf: 1086 wkspath = tempf.name 1087 tempf.write("part " \ 1088 "--source rootfs --ondisk hda --align 4 --fixed-size %d " 1089 "--fstype=ext4\n" % size) 1090 1091 return wkspath 1092 1093 def _get_wic_partitions(self, wkspath, native_sysroot=None, ignore_status=False): 1094 p = runCmd("wic create %s -e core-image-minimal -o %s" % (wkspath, self.resultdir), 1095 ignore_status=ignore_status) 1096 1097 if p.status: 1098 return (p, None) 1099 1100 wksname = os.path.splitext(os.path.basename(wkspath))[0] 1101 1102 wicout = glob(os.path.join(self.resultdir, "%s-*direct" % wksname)) 1103 1104 if not wicout: 1105 return (p, None) 1106 1107 wicimg = wicout[0] 1108 1109 if not native_sysroot: 1110 native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools") 1111 1112 # verify partition size with wic 1113 res = runCmd("parted -m %s unit kib p" % wicimg, 1114 native_sysroot=native_sysroot, stderr=subprocess.PIPE) 1115 1116 # parse parted output which looks like this: 1117 # BYT;\n 1118 # /var/tmp/wic/build/tmpfwvjjkf_-201611101222-hda.direct:200MiB:file:512:512:msdos::;\n 1119 # 1:0.00MiB:200MiB:200MiB:ext4::;\n 1120 return (p, res.output.splitlines()[2:]) 1121 1122 def test_fixed_size(self): 1123 """ 1124 Test creation of a simple image with partition size controlled through 1125 --fixed-size flag 1126 """ 1127 wkspath = Wic2._make_fixed_size_wks(200) 1128 _, partlns = self._get_wic_partitions(wkspath) 1129 os.remove(wkspath) 1130 1131 self.assertEqual(partlns, [ 1132 "1:4.00kiB:204804kiB:204800kiB:ext4::;", 1133 ]) 1134 1135 def test_fixed_size_error(self): 1136 """ 1137 Test creation of a simple image with partition size controlled through 1138 --fixed-size flag. The size of partition is intentionally set to 1MiB 1139 in order to trigger an error in wic. 1140 """ 1141 wkspath = Wic2._make_fixed_size_wks(1) 1142 p, _ = self._get_wic_partitions(wkspath, ignore_status=True) 1143 os.remove(wkspath) 1144 1145 self.assertNotEqual(p.status, 0, "wic exited successfully when an error was expected:\n%s" % p.output) 1146 1147 def test_offset(self): 1148 native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools") 1149 1150 with NamedTemporaryFile("w", suffix=".wks") as tempf: 1151 # Test that partitions are placed at the correct offsets, default KB 1152 tempf.write("bootloader --ptable gpt\n" \ 1153 "part / --source rootfs --ondisk hda --offset 32 --fixed-size 200M --fstype=ext4\n" \ 1154 "part /bar --ondisk hda --offset 204832 --fixed-size 100M --fstype=ext4\n") 1155 tempf.flush() 1156 1157 _, partlns = self._get_wic_partitions(tempf.name, native_sysroot) 1158 self.assertEqual(partlns, [ 1159 "1:32.0kiB:204832kiB:204800kiB:ext4:primary:;", 1160 "2:204832kiB:307232kiB:102400kiB:ext4:primary:;", 1161 ]) 1162 1163 with NamedTemporaryFile("w", suffix=".wks") as tempf: 1164 # Test that partitions are placed at the correct offsets, same with explicit KB 1165 tempf.write("bootloader --ptable gpt\n" \ 1166 "part / --source rootfs --ondisk hda --offset 32K --fixed-size 200M --fstype=ext4\n" \ 1167 "part /bar --ondisk hda --offset 204832K --fixed-size 100M --fstype=ext4\n") 1168 tempf.flush() 1169 1170 _, partlns = self._get_wic_partitions(tempf.name, native_sysroot) 1171 self.assertEqual(partlns, [ 1172 "1:32.0kiB:204832kiB:204800kiB:ext4:primary:;", 1173 "2:204832kiB:307232kiB:102400kiB:ext4:primary:;", 1174 ]) 1175 1176 with NamedTemporaryFile("w", suffix=".wks") as tempf: 1177 # Test that partitions are placed at the correct offsets using MB 1178 tempf.write("bootloader --ptable gpt\n" \ 1179 "part / --source rootfs --ondisk hda --offset 32K --fixed-size 200M --fstype=ext4\n" \ 1180 "part /bar --ondisk hda --offset 201M --fixed-size 100M --fstype=ext4\n") 1181 tempf.flush() 1182 1183 _, partlns = self._get_wic_partitions(tempf.name, native_sysroot) 1184 self.assertEqual(partlns, [ 1185 "1:32.0kiB:204832kiB:204800kiB:ext4:primary:;", 1186 "2:205824kiB:308224kiB:102400kiB:ext4:primary:;", 1187 ]) 1188 1189 with NamedTemporaryFile("w", suffix=".wks") as tempf: 1190 # Test that partitions can be placed on a 512 byte sector boundary 1191 tempf.write("bootloader --ptable gpt\n" \ 1192 "part / --source rootfs --ondisk hda --offset 65s --fixed-size 199M --fstype=ext4\n" \ 1193 "part /bar --ondisk hda --offset 204832 --fixed-size 100M --fstype=ext4\n") 1194 tempf.flush() 1195 1196 _, partlns = self._get_wic_partitions(tempf.name, native_sysroot) 1197 self.assertEqual(partlns, [ 1198 "1:32.5kiB:203808kiB:203776kiB:ext4:primary:;", 1199 "2:204832kiB:307232kiB:102400kiB:ext4:primary:;", 1200 ]) 1201 1202 with NamedTemporaryFile("w", suffix=".wks") as tempf: 1203 # Test that a partition can be placed immediately after a MSDOS partition table 1204 tempf.write("bootloader --ptable msdos\n" \ 1205 "part / --source rootfs --ondisk hda --offset 1s --fixed-size 200M --fstype=ext4\n") 1206 tempf.flush() 1207 1208 _, partlns = self._get_wic_partitions(tempf.name, native_sysroot) 1209 self.assertEqual(partlns, [ 1210 "1:0.50kiB:204800kiB:204800kiB:ext4::;", 1211 ]) 1212 1213 with NamedTemporaryFile("w", suffix=".wks") as tempf: 1214 # Test that image creation fails if the partitions would overlap 1215 tempf.write("bootloader --ptable gpt\n" \ 1216 "part / --source rootfs --ondisk hda --offset 32 --fixed-size 200M --fstype=ext4\n" \ 1217 "part /bar --ondisk hda --offset 204831 --fixed-size 100M --fstype=ext4\n") 1218 tempf.flush() 1219 1220 p, _ = self._get_wic_partitions(tempf.name, ignore_status=True) 1221 self.assertNotEqual(p.status, 0, "wic exited successfully when an error was expected:\n%s" % p.output) 1222 1223 with NamedTemporaryFile("w", suffix=".wks") as tempf: 1224 # Test that partitions are not allowed to overlap with the booloader 1225 tempf.write("bootloader --ptable gpt\n" \ 1226 "part / --source rootfs --ondisk hda --offset 8 --fixed-size 200M --fstype=ext4\n") 1227 tempf.flush() 1228 1229 p, _ = self._get_wic_partitions(tempf.name, ignore_status=True) 1230 self.assertNotEqual(p.status, 0, "wic exited successfully when an error was expected:\n%s" % p.output) 1231 1232 def test_extra_space(self): 1233 native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools") 1234 1235 with NamedTemporaryFile("w", suffix=".wks") as tempf: 1236 tempf.write("bootloader --ptable gpt\n" \ 1237 "part / --source rootfs --ondisk hda --extra-space 200M --fstype=ext4\n") 1238 tempf.flush() 1239 1240 _, partlns = self._get_wic_partitions(tempf.name, native_sysroot) 1241 self.assertEqual(len(partlns), 1) 1242 size = partlns[0].split(':')[3] 1243 self.assertRegex(size, r'^[0-9]+kiB$') 1244 size = int(size[:-3]) 1245 self.assertGreaterEqual(size, 204800) 1246 1247 # TODO this test could also work on aarch64 1248 @skipIfNotArch(['i586', 'i686', 'x86_64']) 1249 @OETestTag("runqemu") 1250 def test_rawcopy_plugin_qemu(self): 1251 """Test rawcopy plugin in qemu""" 1252 # build ext4 and then use it for a wic image 1253 config = 'IMAGE_FSTYPES = "ext4"\n' 1254 self.append_config(config) 1255 bitbake('core-image-minimal') 1256 image_link_name = get_bb_var('IMAGE_LINK_NAME', 'core-image-minimal') 1257 self.remove_config(config) 1258 1259 config = 'IMAGE_FSTYPES = "wic"\n' \ 1260 'IMAGE_LINK_NAME_CORE_IMAGE_MINIMAL = "%s"\n'\ 1261 'WKS_FILE = "test_rawcopy_plugin.wks.in"\n'\ 1262 % image_link_name 1263 self.append_config(config) 1264 bitbake('core-image-minimal-mtdutils') 1265 self.remove_config(config) 1266 1267 runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-minimal-mtdutils') or "" 1268 with runqemu('core-image-minimal-mtdutils', ssh=False, 1269 runqemuparams='%s nographic' % (runqemu_params), image_fstype='wic') as qemu: 1270 cmd = "grep sda. /proc/partitions |wc -l" 1271 status, output = qemu.run_serial(cmd) 1272 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) 1273 self.assertEqual(output, '2') 1274 1275 def _rawcopy_plugin(self, fstype): 1276 """Test rawcopy plugin""" 1277 image = 'core-image-minimal' 1278 bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image) 1279 params = ',unpack' if fstype.endswith('.gz') else '' 1280 with NamedTemporaryFile("w", suffix=".wks") as wks: 1281 wks.write('part / --source rawcopy --sourceparams="file=%s.%s%s"\n'\ 1282 % (bb_vars['IMAGE_LINK_NAME'], fstype, params)) 1283 wks.flush() 1284 cmd = "wic create %s -e %s -o %s" % (wks.name, image, self.resultdir) 1285 runCmd(cmd) 1286 wksname = os.path.splitext(os.path.basename(wks.name))[0] 1287 out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname)) 1288 self.assertEqual(1, len(out)) 1289 1290 def test_rawcopy_plugin(self): 1291 config = 'IMAGE_FSTYPES = "ext4"\n' 1292 self.append_config(config) 1293 self.assertEqual(0, bitbake('core-image-minimal').status) 1294 self.remove_config(config) 1295 self._rawcopy_plugin('ext4') 1296 1297 def test_rawcopy_plugin_unpack(self): 1298 fstype = 'ext4.gz' 1299 config = 'IMAGE_FSTYPES = "%s"\n' % fstype 1300 self.append_config(config) 1301 self.assertEqual(0, bitbake('core-image-minimal').status) 1302 self.remove_config(config) 1303 self._rawcopy_plugin(fstype) 1304 1305 def test_empty_plugin(self): 1306 """Test empty plugin""" 1307 config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "test_empty_plugin.wks"\n' 1308 self.append_config(config) 1309 image = 'core-image-minimal' 1310 bitbake(image) 1311 self.remove_config(config) 1312 bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image) 1313 image_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], '%s.wic' % bb_vars['IMAGE_LINK_NAME']) 1314 self.assertTrue(os.path.exists(image_path), msg="Image file %s wasn't generated as expected" % image_path) 1315 1316 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools') 1317 1318 # Fstype column from 'wic ls' should be empty for the second partition 1319 # as listed in test_empty_plugin.wks 1320 result = runCmd("wic ls %s -n %s | awk -F ' ' '{print $1 \" \" $5}' | grep '^2' | wc -w" % (image_path, sysroot)) 1321 self.assertEqual('1', result.output) 1322 1323 @skipIfNotArch(['i586', 'i686', 'x86_64']) 1324 @OETestTag("runqemu") 1325 def test_biosplusefi_plugin_qemu(self): 1326 """Test biosplusefi plugin in qemu""" 1327 config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "test_biosplusefi_plugin.wks"\nMACHINE_FEATURES:append = " efi"\n' 1328 self.append_config(config) 1329 bitbake('core-image-minimal') 1330 self.remove_config(config) 1331 1332 runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-minimal') or "" 1333 with runqemu('core-image-minimal', ssh=False, 1334 runqemuparams='%s nographic' % (runqemu_params), image_fstype='wic') as qemu: 1335 # Check that we have ONLY two /dev/sda* partitions (/boot and /) 1336 cmd = "grep sda. /proc/partitions | wc -l" 1337 status, output = qemu.run_serial(cmd) 1338 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) 1339 self.assertEqual(output, '2') 1340 # Check that /dev/sda1 is /boot and that either /dev/root OR /dev/sda2 is / 1341 cmd = "mount | grep '^/dev/' | cut -f1,3 -d ' ' | egrep -c -e '/dev/sda1 /boot' -e '/dev/root /|/dev/sda2 /'" 1342 status, output = qemu.run_serial(cmd) 1343 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) 1344 self.assertEqual(output, '2') 1345 # Check that /boot has EFI bootx64.efi (required for EFI) 1346 cmd = "ls /boot/EFI/BOOT/bootx64.efi | wc -l" 1347 status, output = qemu.run_serial(cmd) 1348 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) 1349 self.assertEqual(output, '1') 1350 # Check that "BOOTABLE" flag is set on boot partition (required for PC-Bios) 1351 # Trailing "cat" seems to be required; otherwise run_serial() sends back echo of the input command 1352 cmd = "fdisk -l /dev/sda | grep /dev/sda1 | awk {print'$2'} | cat" 1353 status, output = qemu.run_serial(cmd) 1354 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) 1355 self.assertEqual(output, '*') 1356 1357 @skipIfNotArch(['i586', 'i686', 'x86_64']) 1358 def test_biosplusefi_plugin(self): 1359 """Test biosplusefi plugin""" 1360 # Wic generation below may fail depending on the order of the unittests 1361 # This is because bootimg-pcbios (that bootimg-biosplusefi uses) generate its MBR inside STAGING_DATADIR directory 1362 # which may or may not exists depending on what was built already 1363 # If an image hasn't been built yet, directory ${STAGING_DATADIR}/syslinux won't exists and _get_bootimg_dir() 1364 # will raise with "Couldn't find correct bootimg_dir" 1365 # The easiest way to work-around this issue is to make sure we already built an image here, hence the bitbake call 1366 config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "test_biosplusefi_plugin.wks"\nMACHINE_FEATURES:append = " efi"\n' 1367 self.append_config(config) 1368 bitbake('core-image-minimal') 1369 self.remove_config(config) 1370 1371 img = 'core-image-minimal' 1372 with NamedTemporaryFile("w", suffix=".wks") as wks: 1373 wks.writelines(['part /boot --active --source bootimg-biosplusefi --sourceparams="loader=grub-efi"\n', 1374 'part / --source rootfs --fstype=ext4 --align 1024 --use-uuid\n'\ 1375 'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n']) 1376 wks.flush() 1377 cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir) 1378 runCmd(cmd) 1379 wksname = os.path.splitext(os.path.basename(wks.name))[0] 1380 out = glob(os.path.join(self.resultdir, "%s-*.direct" % wksname)) 1381 self.assertEqual(1, len(out)) 1382 1383 @skipIfNotArch(['i586', 'i686', 'x86_64', 'aarch64']) 1384 def test_uefi_kernel(self): 1385 """ Test uefi-kernel in wic """ 1386 config = 'IMAGE_EFI_BOOT_FILES="/etc/fstab;testfile"\nIMAGE_FSTYPES = "wic"\nWKS_FILE = "test_uefikernel.wks"\nMACHINE_FEATURES:append = " efi"\n' 1387 self.append_config(config) 1388 bitbake('core-image-minimal') 1389 self.remove_config(config) 1390 1391 img = 'core-image-minimal' 1392 with NamedTemporaryFile("w", suffix=".wks") as wks: 1393 wks.writelines(['part /boot --source bootimg-efi --sourceparams="loader=uefi-kernel"\n' 1394 'part / --source rootfs --fstype=ext4 --align 1024 --use-uuid\n'\ 1395 'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n']) 1396 wks.flush() 1397 cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir) 1398 runCmd(cmd) 1399 wksname = os.path.splitext(os.path.basename(wks.name))[0] 1400 out = glob(os.path.join(self.resultdir, "%s-*.direct" % wksname)) 1401 self.assertEqual(1, len(out)) 1402 1403 # TODO this test could also work on aarch64 1404 @skipIfNotArch(['i586', 'i686', 'x86_64']) 1405 @OETestTag("runqemu") 1406 def test_efi_plugin_unified_kernel_image_qemu(self): 1407 """Test Unified Kernel Image feature in qemu without systemd in initramfs or rootfs""" 1408 config = """ 1409# efi firmware must load systemd-boot, not grub 1410EFI_PROVIDER = "systemd-boot" 1411 1412# image format must be wic, needs esp partition for firmware etc 1413IMAGE_FSTYPES:pn-core-image-base:append = " wic" 1414WKS_FILE = "test_efi_plugin.wks" 1415 1416# efi, uki and systemd features must be enabled 1417MACHINE_FEATURES:append = " efi" 1418IMAGE_CLASSES:append:pn-core-image-base = " uki" 1419 1420# uki embeds also an initrd, no systemd or udev 1421INITRAMFS_IMAGE = "core-image-initramfs-boot" 1422 1423# runqemu must not load kernel separately, it's in the uki 1424QB_KERNEL_ROOT = "" 1425QB_DEFAULT_KERNEL = "none" 1426 1427# boot command line provided via uki, not via bootloader 1428UKI_CMDLINE = "rootwait root=LABEL=root console=${KERNEL_CONSOLE}" 1429 1430""" 1431 self.append_config(config) 1432 bitbake('core-image-base ovmf') 1433 runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-base') or "" 1434 uki_filename = get_bb_var('UKI_FILENAME', 'core-image-base') 1435 self.remove_config(config) 1436 1437 with runqemu('core-image-base', ssh=False, 1438 runqemuparams='%s nographic ovmf' % (runqemu_params), image_fstype='wic') as qemu: 1439 # Check that /boot has EFI boot*.efi (required for EFI) 1440 cmd = "ls /boot/EFI/BOOT/boot*.efi | wc -l" 1441 status, output = qemu.run_serial(cmd) 1442 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) 1443 self.assertEqual(output, '1') 1444 # Check that /boot has EFI/Linux/${UKI_FILENAME} (required for Unified Kernel Images auto detection) 1445 cmd = "ls /boot/EFI/Linux/%s | wc -l" % (uki_filename) 1446 status, output = qemu.run_serial(cmd) 1447 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) 1448 self.assertEqual(output, '1') 1449 # Check that /boot doesn't have loader/entries/boot.conf (Unified Kernel Images are auto detected by the bootloader) 1450 cmd = "ls /boot/loader/entries/boot.conf 2&>/dev/null | wc -l" 1451 status, output = qemu.run_serial(cmd) 1452 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) 1453 self.assertEqual(output, '0') 1454 1455 @skipIfNotArch(['aarch64']) 1456 @OETestTag("runqemu") 1457 def test_efi_plugin_plain_systemd_boot_qemu_aarch64(self): 1458 """Test plain systemd-boot in qemu with systemd""" 1459 config = """ 1460INIT_MANAGER = "systemd" 1461EFI_PROVIDER = "systemd-boot" 1462 1463# image format must be wic, needs esp partition for firmware etc 1464IMAGE_FSTYPES:pn-core-image-base:append = " wic" 1465WKS_FILE = "test_efi_plugin_plain_systemd-boot.wks" 1466 1467INITRAMFS_IMAGE = "core-image-initramfs-boot" 1468 1469# to configure runqemu 1470IMAGE_CLASSES += "qemuboot" 1471# u-boot efi firmware 1472QB_DEFAULT_BIOS = "u-boot.bin" 1473# need to use virtio, scsi not supported by u-boot by default 1474QB_DRIVE_TYPE = "/dev/vd" 1475 1476# disable kvm, breaks boot 1477QEMU_USE_KVM = "" 1478 1479IMAGE_CLASSES:remove = 'testimage' 1480""" 1481 self.append_config(config) 1482 bitbake('core-image-base u-boot') 1483 runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-base') or "" 1484 1485 with runqemu('core-image-base', ssh=False, 1486 runqemuparams='%s nographic' % (runqemu_params), image_fstype='wic') as qemu: 1487 # Check that /boot has EFI boot*.efi (required for EFI) 1488 cmd = "ls /boot/EFI/BOOT/boot*.efi | wc -l" 1489 status, output = qemu.run_serial(cmd) 1490 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) 1491 self.assertEqual(output, '1') 1492 # Check that boot.conf exists 1493 cmd = "cat /boot/loader/entries/boot.conf" 1494 status, output = qemu.run_serial(cmd) 1495 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) 1496 self.remove_config(config) 1497 1498 @skipIfNotArch(['i586', 'i686', 'x86_64']) 1499 @OETestTag("runqemu") 1500 def test_efi_plugin_plain_systemd_boot_qemu_x86(self): 1501 """Test plain systemd-boot to systemd in qemu""" 1502 config = """ 1503INIT_MANAGER = "systemd" 1504EFI_PROVIDER = "systemd-boot" 1505 1506# image format must be wic, needs esp partition for firmware etc 1507IMAGE_FSTYPES:pn-core-image-base:append = " wic" 1508WKS_FILE = "test_efi_plugin_plain_systemd-boot.wks" 1509 1510INITRAMFS_IMAGE = "core-image-initramfs-boot" 1511""" 1512 self.append_config(config) 1513 bitbake('core-image-base ovmf') 1514 runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-base') or "" 1515 self.remove_config(config) 1516 1517 with runqemu('core-image-base', ssh=False, 1518 runqemuparams='%s nographic ovmf' % (runqemu_params), image_fstype='wic') as qemu: 1519 # Check that /boot has EFI boot*.efi (required for EFI) 1520 cmd = "ls /boot/EFI/BOOT/boot*.efi | wc -l" 1521 status, output = qemu.run_serial(cmd) 1522 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) 1523 self.assertEqual(output, '1') 1524 # Check that boot.conf exists 1525 cmd = "cat /boot/loader/entries/boot.conf" 1526 status, output = qemu.run_serial(cmd) 1527 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) 1528 1529 def test_fs_types(self): 1530 """Test filesystem types for empty and not empty partitions""" 1531 img = 'core-image-minimal' 1532 with NamedTemporaryFile("w", suffix=".wks") as wks: 1533 wks.writelines(['part ext2 --fstype ext2 --source rootfs\n', 1534 'part btrfs --fstype btrfs --source rootfs --size 40M\n', 1535 'part squash --fstype squashfs --source rootfs\n', 1536 'part swap --fstype swap --size 1M\n', 1537 'part emptyvfat --fstype vfat --size 1M\n', 1538 'part emptymsdos --fstype msdos --size 1M\n', 1539 'part emptyext2 --fstype ext2 --size 1M\n', 1540 'part emptybtrfs --fstype btrfs --size 150M\n']) 1541 wks.flush() 1542 cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir) 1543 runCmd(cmd) 1544 wksname = os.path.splitext(os.path.basename(wks.name))[0] 1545 out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname)) 1546 self.assertEqual(1, len(out)) 1547 1548 def test_kickstart_parser(self): 1549 """Test wks parser options""" 1550 with NamedTemporaryFile("w", suffix=".wks") as wks: 1551 wks.writelines(['part / --fstype ext3 --source rootfs --system-id 0xFF '\ 1552 '--overhead-factor 1.2 --size 100k\n']) 1553 wks.flush() 1554 cmd = "wic create %s -e core-image-minimal -o %s" % (wks.name, self.resultdir) 1555 runCmd(cmd) 1556 wksname = os.path.splitext(os.path.basename(wks.name))[0] 1557 out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname)) 1558 self.assertEqual(1, len(out)) 1559 1560 def test_image_bootpart_globbed(self): 1561 """Test globbed sources with image-bootpart plugin""" 1562 img = "core-image-minimal" 1563 cmd = "wic create sdimage-bootpart -e %s -o %s" % (img, self.resultdir) 1564 config = 'IMAGE_BOOT_FILES = "%s*"' % get_bb_var('KERNEL_IMAGETYPE', img) 1565 self.append_config(config) 1566 runCmd(cmd) 1567 self.remove_config(config) 1568 self.assertEqual(1, len(glob(os.path.join(self.resultdir, "sdimage-bootpart-*direct")))) 1569 1570 def test_sparse_copy(self): 1571 """Test sparse_copy with FIEMAP and SEEK_HOLE filemap APIs""" 1572 libpath = os.path.join(self.td['COREBASE'], 'scripts', 'lib', 'wic') 1573 sys.path.insert(0, libpath) 1574 from filemap import FilemapFiemap, FilemapSeek, sparse_copy, ErrorNotSupp 1575 with NamedTemporaryFile("w", suffix=".wic-sparse") as sparse: 1576 src_name = sparse.name 1577 src_size = 1024 * 10 1578 sparse.truncate(src_size) 1579 # write one byte to the file 1580 with open(src_name, 'r+b') as sfile: 1581 sfile.seek(1024 * 4) 1582 sfile.write(b'\x00') 1583 dest = sparse.name + '.out' 1584 # copy src file to dest using different filemap APIs 1585 for api in (FilemapFiemap, FilemapSeek, None): 1586 if os.path.exists(dest): 1587 os.unlink(dest) 1588 try: 1589 sparse_copy(sparse.name, dest, api=api) 1590 except ErrorNotSupp: 1591 continue # skip unsupported API 1592 dest_stat = os.stat(dest) 1593 self.assertEqual(dest_stat.st_size, src_size) 1594 # 8 blocks is 4K (physical sector size) 1595 self.assertEqual(dest_stat.st_blocks, 8) 1596 os.unlink(dest) 1597 1598 def test_mkfs_extraopts(self): 1599 """Test wks option --mkfs-extraopts for empty and not empty partitions""" 1600 img = 'core-image-minimal' 1601 with NamedTemporaryFile("w", suffix=".wks") as wks: 1602 wks.writelines( 1603 ['part ext2 --fstype ext2 --source rootfs --mkfs-extraopts "-D -F -i 8192"\n', 1604 "part btrfs --fstype btrfs --source rootfs --size 40M --mkfs-extraopts='--quiet'\n", 1605 'part squash --fstype squashfs --source rootfs --mkfs-extraopts "-no-sparse -b 4096"\n', 1606 'part emptyvfat --fstype vfat --size 1M --mkfs-extraopts "-S 1024 -s 64"\n', 1607 'part emptymsdos --fstype msdos --size 1M --mkfs-extraopts "-S 1024 -s 64"\n', 1608 'part emptyext2 --fstype ext2 --size 1M --mkfs-extraopts "-D -F -i 8192"\n', 1609 'part emptybtrfs --fstype btrfs --size 100M --mkfs-extraopts "--mixed -K"\n']) 1610 wks.flush() 1611 cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir) 1612 runCmd(cmd) 1613 wksname = os.path.splitext(os.path.basename(wks.name))[0] 1614 out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname)) 1615 self.assertEqual(1, len(out)) 1616 1617 @skipIfNotArch(['i586', 'i686', 'x86_64']) 1618 @OETestTag("runqemu") 1619 def test_expand_mbr_image(self): 1620 """Test wic write --expand command for mbr image""" 1621 # build an image 1622 config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "directdisk.wks"\n' 1623 self.append_config(config) 1624 image = 'core-image-minimal' 1625 bitbake(image) 1626 1627 # get path to the 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 self.remove_config(config) 1632 1633 try: 1634 # expand image to 1G 1635 new_image_path = None 1636 with NamedTemporaryFile(mode='wb', suffix='.wic.exp', 1637 dir=bb_vars['DEPLOY_DIR_IMAGE'], delete=False) as sparse: 1638 sparse.truncate(1024 ** 3) 1639 new_image_path = sparse.name 1640 1641 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools') 1642 cmd = "wic write -n %s --expand 1:0 %s %s" % (sysroot, image_path, new_image_path) 1643 runCmd(cmd) 1644 1645 # check if partitions are expanded 1646 orig = runCmd("wic ls %s -n %s" % (image_path, sysroot)) 1647 exp = runCmd("wic ls %s -n %s" % (new_image_path, sysroot)) 1648 orig_sizes = [int(line.split()[3]) for line in orig.output.split('\n')[1:]] 1649 exp_sizes = [int(line.split()[3]) for line in exp.output.split('\n')[1:]] 1650 self.assertEqual(orig_sizes[0], exp_sizes[0]) # first partition is not resized 1651 self.assertTrue(orig_sizes[1] < exp_sizes[1], msg="Parition size wasn't enlarged (%s vs %s)" % (orig_sizes[1], exp_sizes[1])) 1652 1653 # Check if all free space is partitioned 1654 result = runCmd("%s/usr/sbin/sfdisk -F %s" % (sysroot, new_image_path)) 1655 self.assertIn("0 B, 0 bytes, 0 sectors", result.output) 1656 1657 os.rename(image_path, image_path + '.bak') 1658 os.rename(new_image_path, image_path) 1659 1660 runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-minimal') or "" 1661 with runqemu('core-image-minimal', ssh=False, runqemuparams='%s nographic' % (runqemu_params)) as qemu: 1662 cmd = "ls /etc/" 1663 status, output = qemu.run_serial('true') 1664 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) 1665 finally: 1666 if os.path.exists(new_image_path): 1667 os.unlink(new_image_path) 1668 if os.path.exists(image_path + '.bak'): 1669 os.rename(image_path + '.bak', image_path) 1670 1671 def test_gpt_partition_name(self): 1672 """Test --part-name argument to set partition name in GPT table""" 1673 config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "test_gpt_partition_name.wks"\n' 1674 self.append_config(config) 1675 image = 'core-image-minimal' 1676 bitbake(image) 1677 self.remove_config(config) 1678 deploy_dir = get_bb_var('DEPLOY_DIR_IMAGE') 1679 bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image) 1680 image_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], '%s.wic' % bb_vars['IMAGE_LINK_NAME']) 1681 1682 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools') 1683 1684 # Image is created 1685 self.assertTrue(os.path.exists(image_path), "image file %s doesn't exist" % image_path) 1686 1687 # Check the names of the three partitions 1688 # as listed in test_gpt_partition_name.wks 1689 result = runCmd("%s/usr/sbin/sfdisk --part-label %s 1" % (sysroot, image_path)) 1690 self.assertEqual('boot-A', result.output) 1691 result = runCmd("%s/usr/sbin/sfdisk --part-label %s 2" % (sysroot, image_path)) 1692 self.assertEqual('root-A', result.output) 1693 # When the --part-name is not defined, the partition name is equal to the --label 1694 result = runCmd("%s/usr/sbin/sfdisk --part-label %s 3" % (sysroot, image_path)) 1695 self.assertEqual('ext-space', result.output) 1696 1697 def test_empty_zeroize_plugin(self): 1698 img = 'core-image-minimal' 1699 expected_size = [ 1024*1024, # 1M 1700 512*1024, # 512K 1701 2*1024*1024] # 2M 1702 # Check combination of sourceparams 1703 with NamedTemporaryFile("w", suffix=".wks") as wks: 1704 wks.writelines( 1705 ['part empty --source empty --sourceparams="fill" --ondisk sda --fixed-size 1M\n', 1706 'part empty --source empty --sourceparams="size=512K" --ondisk sda --size 1M --align 1024\n', 1707 'part empty --source empty --sourceparams="size=2048k,bs=512K" --ondisk sda --size 4M --align 1024\n' 1708 ]) 1709 wks.flush() 1710 cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir) 1711 runCmd(cmd) 1712 wksname = os.path.splitext(os.path.basename(wks.name))[0] 1713 wicout = glob(os.path.join(self.resultdir, "%s-*direct" % wksname)) 1714 # Skip the complete image and just look at the single partitions 1715 for idx, value in enumerate(wicout[1:]): 1716 self.logger.info(wicout[idx]) 1717 # Check if partitions are actually zeroized 1718 with open(wicout[idx], mode="rb") as fd: 1719 ba = bytearray(fd.read()) 1720 for b in ba: 1721 self.assertEqual(b, 0) 1722 self.assertEqual(expected_size[idx], os.path.getsize(wicout[idx])) 1723 1724 # Check inconsistancy check between "fill" and "--size" parameter 1725 with NamedTemporaryFile("w", suffix=".wks") as wks: 1726 wks.writelines(['part empty --source empty --sourceparams="fill" --ondisk sda --size 1M\n']) 1727 wks.flush() 1728 cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir) 1729 result = runCmd(cmd, ignore_status=True) 1730 self.assertIn("Source parameter 'fill' only works with the '--fixed-size' option, exiting.", result.output) 1731 self.assertNotEqual(0, result.status) 1732 1733class ModifyTests(WicTestCase): 1734 def test_wic_ls(self): 1735 """Test listing image content using 'wic ls'""" 1736 runCmd("wic create wictestdisk " 1737 "--image-name=core-image-minimal " 1738 "-D -o %s" % self.resultdir) 1739 images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct")) 1740 self.assertEqual(1, len(images)) 1741 1742 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools') 1743 1744 # list partitions 1745 result = runCmd("wic ls %s -n %s" % (images[0], sysroot)) 1746 self.assertEqual(3, len(result.output.split('\n'))) 1747 1748 # list directory content of the first partition 1749 result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot)) 1750 self.assertEqual(6, len(result.output.split('\n'))) 1751 1752 def test_wic_cp(self): 1753 """Test copy files and directories to the the wic image.""" 1754 runCmd("wic create wictestdisk " 1755 "--image-name=core-image-minimal " 1756 "-D -o %s" % self.resultdir) 1757 images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct")) 1758 self.assertEqual(1, len(images)) 1759 1760 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools') 1761 1762 # list directory content of the first partition 1763 result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot)) 1764 self.assertEqual(6, len(result.output.split('\n'))) 1765 1766 with NamedTemporaryFile("w", suffix=".wic-cp") as testfile: 1767 testfile.write("test") 1768 1769 # copy file to the partition 1770 runCmd("wic cp %s %s:1/ -n %s" % (testfile.name, images[0], sysroot)) 1771 1772 # check if file is there 1773 result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot)) 1774 self.assertEqual(7, len(result.output.split('\n'))) 1775 self.assertIn(os.path.basename(testfile.name), result.output) 1776 1777 # prepare directory 1778 testdir = os.path.join(self.resultdir, 'wic-test-cp-dir') 1779 testsubdir = os.path.join(testdir, 'subdir') 1780 os.makedirs(os.path.join(testsubdir)) 1781 copy(testfile.name, testdir) 1782 1783 # copy directory to the partition 1784 runCmd("wic cp %s %s:1/ -n %s" % (testdir, images[0], sysroot)) 1785 1786 # check if directory is there 1787 result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot)) 1788 self.assertEqual(8, len(result.output.split('\n'))) 1789 self.assertIn(os.path.basename(testdir), result.output) 1790 1791 # copy the file from the partition and check if it success 1792 dest = '%s-cp' % testfile.name 1793 runCmd("wic cp %s:1/%s %s -n %s" % (images[0], 1794 os.path.basename(testfile.name), dest, sysroot)) 1795 self.assertTrue(os.path.exists(dest), msg="File %s wasn't generated as expected" % dest) 1796 1797 1798 def test_wic_rm(self): 1799 """Test removing files and directories from the the wic image.""" 1800 runCmd("wic create mkefidisk " 1801 "--image-name=core-image-minimal " 1802 "-D -o %s" % self.resultdir) 1803 images = glob(os.path.join(self.resultdir, "mkefidisk-*.direct")) 1804 self.assertEqual(1, len(images)) 1805 1806 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools') 1807 # Not bulletproof but hopefully sufficient 1808 kerneltype = get_bb_var('KERNEL_IMAGETYPE', 'virtual/kernel') 1809 1810 # list directory content of the first partition 1811 result = runCmd("wic ls %s:1 -n %s" % (images[0], sysroot)) 1812 self.assertIn('\n%s ' % kerneltype.upper(), result.output) 1813 self.assertIn('\nEFI <DIR> ', result.output) 1814 1815 # remove file. EFI partitions are case-insensitive so exercise that too 1816 runCmd("wic rm %s:1/%s -n %s" % (images[0], kerneltype.lower(), sysroot)) 1817 1818 # remove directory 1819 runCmd("wic rm %s:1/efi -n %s" % (images[0], sysroot)) 1820 1821 # check if they're removed 1822 result = runCmd("wic ls %s:1 -n %s" % (images[0], sysroot)) 1823 self.assertNotIn('\n%s ' % kerneltype.upper(), result.output) 1824 self.assertNotIn('\nEFI <DIR> ', result.output) 1825 1826 def test_wic_ls_ext(self): 1827 """Test listing content of the ext partition using 'wic ls'""" 1828 runCmd("wic create wictestdisk " 1829 "--image-name=core-image-minimal " 1830 "-D -o %s" % self.resultdir) 1831 images = glob(os.path.join(self.resultdir, "wictestdisk-*.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 second ext4 partition 1837 result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot)) 1838 self.assertTrue(set(['bin', 'home', 'proc', 'usr', 'var', 'dev', 'lib', 'sbin']).issubset( 1839 set(line.split()[-1] for line in result.output.split('\n') if line)), msg="Expected directories not present %s" % result.output) 1840 1841 def test_wic_cp_ext(self): 1842 """Test copy files and directories to the ext partition.""" 1843 runCmd("wic create wictestdisk " 1844 "--image-name=core-image-minimal " 1845 "-D -o %s" % self.resultdir) 1846 images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct")) 1847 self.assertEqual(1, len(images)) 1848 1849 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools') 1850 1851 # list directory content of the ext4 partition 1852 result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot)) 1853 dirs = set(line.split()[-1] for line in result.output.split('\n') if line) 1854 self.assertTrue(set(['bin', 'home', 'proc', 'usr', 'var', 'dev', 'lib', 'sbin']).issubset(dirs), msg="Expected directories not present %s" % dirs) 1855 1856 with NamedTemporaryFile("w", suffix=".wic-cp") as testfile: 1857 testfile.write("test") 1858 1859 # copy file to the partition 1860 runCmd("wic cp %s %s:2/ -n %s" % (testfile.name, images[0], sysroot)) 1861 1862 # check if file is there 1863 result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot)) 1864 newdirs = set(line.split()[-1] for line in result.output.split('\n') if line) 1865 self.assertEqual(newdirs.difference(dirs), set([os.path.basename(testfile.name)])) 1866 1867 # check if the file to copy is in the partition 1868 result = runCmd("wic ls %s:2/etc/ -n %s" % (images[0], sysroot)) 1869 self.assertIn('fstab', [line.split()[-1] for line in result.output.split('\n') if line]) 1870 1871 # copy file from the partition, replace the temporary file content with it and 1872 # check for the file size to validate the copy 1873 runCmd("wic cp %s:2/etc/fstab %s -n %s" % (images[0], testfile.name, sysroot)) 1874 self.assertTrue(os.stat(testfile.name).st_size > 0, msg="Filesize not as expected %s" % os.stat(testfile.name).st_size) 1875 1876 1877 def test_wic_rm_ext(self): 1878 """Test removing files from the ext partition.""" 1879 runCmd("wic create mkefidisk " 1880 "--image-name=core-image-minimal " 1881 "-D -o %s" % self.resultdir) 1882 images = glob(os.path.join(self.resultdir, "mkefidisk-*.direct")) 1883 self.assertEqual(1, len(images)) 1884 1885 sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools') 1886 1887 # list directory content of the /etc directory on ext4 partition 1888 result = runCmd("wic ls %s:2/etc/ -n %s" % (images[0], sysroot)) 1889 self.assertIn('fstab', [line.split()[-1] for line in result.output.split('\n') if line]) 1890 1891 # remove file 1892 runCmd("wic rm %s:2/etc/fstab -n %s" % (images[0], sysroot)) 1893 1894 # check if it's removed 1895 result = runCmd("wic ls %s:2/etc/ -n %s" % (images[0], sysroot)) 1896 self.assertNotIn('fstab', [line.split()[-1] for line in result.output.split('\n') if line]) 1897 1898 # remove non-empty directory 1899 runCmd("wic rm -r %s:2/etc/ -n %s" % (images[0], sysroot)) 1900 1901 # check if it's removed 1902 result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot)) 1903 self.assertNotIn('etc', [line.split()[-1] for line in result.output.split('\n') if line]) 1904