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