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