1#
2# SPDX-License-Identifier: MIT
3#
4
5from oeqa.selftest.case import OESelftestTestCase
6from oeqa.utils.commands import runCmd, bitbake, get_bb_var, runqemu
7from oeqa.utils.sshcontrol import SSHControl
8import glob
9import os
10import json
11
12class ImageFeatures(OESelftestTestCase):
13
14    test_user = 'tester'
15    root_user = 'root'
16
17    def test_non_root_user_can_connect_via_ssh_without_password(self):
18        """
19        Summary: Check if non root user can connect via ssh without password
20        Expected: 1. Connection to the image via ssh using root user without providing a password should be allowed.
21                  2. Connection to the image via ssh using tester user without providing a password should be allowed.
22        Product: oe-core
23        Author: Ionut Chisanovici <ionutx.chisanovici@intel.com>
24        AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
25        """
26
27        features = 'EXTRA_IMAGE_FEATURES = "ssh-server-openssh empty-root-password allow-empty-password allow-root-login"\n'
28        features += 'INHERIT += "extrausers"\n'
29        features += 'EXTRA_USERS_PARAMS = "useradd -p \'\' {}; usermod -s /bin/sh {};"'.format(self.test_user, self.test_user)
30        self.write_config(features)
31
32        # Build a core-image-minimal
33        bitbake('core-image-minimal')
34
35        with runqemu("core-image-minimal") as qemu:
36            # Attempt to ssh with each user into qemu with empty password
37            for user in [self.root_user, self.test_user]:
38                ssh = SSHControl(ip=qemu.ip, logfile=qemu.sshlog, user=user)
39                status, output = ssh.run("true")
40                self.assertEqual(status, 0, 'ssh to user %s failed with %s' % (user, output))
41
42    def test_all_users_can_connect_via_ssh_without_password(self):
43        """
44        Summary:     Check if all users can connect via ssh without password
45        Expected: 1. Connection to the image via ssh using root user without providing a password should NOT be allowed.
46                  2. Connection to the image via ssh using tester user without providing a password should be allowed.
47        Product:     oe-core
48        Author:      Ionut Chisanovici <ionutx.chisanovici@intel.com>
49        AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
50        """
51
52        features = 'EXTRA_IMAGE_FEATURES = "ssh-server-openssh allow-empty-password allow-root-login"\n'
53        features += 'INHERIT += "extrausers"\n'
54        features += 'EXTRA_USERS_PARAMS = "useradd -p \'\' {}; usermod -s /bin/sh {};"'.format(self.test_user, self.test_user)
55        self.write_config(features)
56
57        # Build a core-image-minimal
58        bitbake('core-image-minimal')
59
60        with runqemu("core-image-minimal") as qemu:
61            # Attempt to ssh with each user into qemu with empty password
62            for user in [self.root_user, self.test_user]:
63                ssh = SSHControl(ip=qemu.ip, logfile=qemu.sshlog, user=user)
64                status, output = ssh.run("true")
65                if user == 'root':
66                    self.assertNotEqual(status, 0, 'ssh to user root was allowed when it should not have been')
67                else:
68                    self.assertEqual(status, 0, 'ssh to user tester failed with %s' % output)
69
70
71    def test_wayland_support_in_image(self):
72        """
73        Summary:     Check Wayland support in image
74        Expected:    1. Wayland image can be build
75                     2. Wayland feature can be installed
76        Product:     oe-core
77        Author:      Ionut Chisanovici <ionutx.chisanovici@intel.com>
78        AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
79        """
80
81        distro_features = get_bb_var('DISTRO_FEATURES')
82        if not ('opengl' in distro_features and 'wayland' in distro_features):
83            self.skipTest('neither opengl nor wayland present on DISTRO_FEATURES so core-image-weston cannot be built')
84
85        # Build a core-image-weston
86        bitbake('core-image-weston')
87
88    def test_bmap(self):
89        """
90        Summary:     Check bmap support
91        Expected:    1. core-image-minimal can be build with bmap support
92                     2. core-image-minimal is sparse
93        Product:     oe-core
94        Author:      Ed Bartosh <ed.bartosh@linux.intel.com>
95        """
96
97        features = 'IMAGE_FSTYPES += " ext4 ext4.bmap ext4.bmap.gz"'
98        self.write_config(features)
99
100        image_name = 'core-image-minimal'
101        bitbake(image_name)
102
103        deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
104        link_name = get_bb_var('IMAGE_LINK_NAME', image_name)
105        image_path = os.path.join(deploy_dir_image, "%s.ext4" % link_name)
106        bmap_path = "%s.bmap" % image_path
107        gzip_path = "%s.gz" % bmap_path
108
109        # check if result image, bmap and bmap.gz files are in deploy directory
110        self.assertTrue(os.path.exists(image_path))
111        self.assertTrue(os.path.exists(bmap_path))
112        self.assertTrue(os.path.exists(gzip_path))
113
114        # check if result image is sparse
115        image_stat = os.stat(image_path)
116        self.assertGreater(image_stat.st_size, image_stat.st_blocks * 512)
117
118        # check if the resulting gzip is valid
119        self.assertTrue(runCmd('gzip -t %s' % gzip_path))
120
121    def test_hypervisor_fmts(self):
122        """
123        Summary:     Check various hypervisor formats
124        Expected:    1. core-image-minimal can be built with vmdk, vdi and
125                        qcow2 support.
126                     2. qemu-img says each image has the expected format
127        Product:     oe-core
128        Author:      Tom Rini <trini@konsulko.com>
129        """
130
131        img_types = [ 'vmdk', 'vdi', 'qcow2' ]
132        features = ""
133        for itype in img_types:
134            features += 'IMAGE_FSTYPES += "wic.%s"\n' % itype
135        self.write_config(features)
136
137        image_name = 'core-image-minimal'
138        bitbake(image_name)
139
140        deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
141        link_name = get_bb_var('IMAGE_LINK_NAME', image_name)
142        for itype in img_types:
143            image_path = os.path.join(deploy_dir_image, "%s.wic.%s" %
144                                      (link_name, itype))
145
146            # check if result image file is in deploy directory
147            self.assertTrue(os.path.exists(image_path))
148
149            # check if result image is vmdk
150            sysroot = get_bb_var('STAGING_DIR_NATIVE', 'core-image-minimal')
151            result = runCmd('qemu-img info --output json %s' % image_path,
152                            native_sysroot=sysroot)
153            try:
154                data = json.loads(result.output)
155                self.assertEqual(data.get('format'), itype,
156                                 msg="Unexpected format in '%s'" % (result.output))
157            except json.decoder.JSONDecodeError:
158                self.fail("Could not parse '%ss'" % result.output)
159
160    def test_long_chain_conversion(self):
161        """
162        Summary:     Check for chaining many CONVERSION_CMDs together
163        Expected:    1. core-image-minimal can be built with
164                        ext4.bmap.gz.bz2.lzo.xz.u-boot and also create a
165                        sha256sum
166                     2. The above image has a valid sha256sum
167        Product:     oe-core
168        Author:      Tom Rini <trini@konsulko.com>
169        """
170
171        conv = "ext4.bmap.gz.bz2.lzo.xz.u-boot"
172        features = 'IMAGE_FSTYPES += "%s %s.sha256sum"' % (conv, conv)
173        self.write_config(features)
174
175        image_name = 'core-image-minimal'
176        bitbake(image_name)
177
178        deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
179        link_name = get_bb_var('IMAGE_LINK_NAME', image_name)
180        image_path = os.path.join(deploy_dir_image, "%s.%s" %
181                                  (link_name, conv))
182
183        # check if resulting image is in the deploy directory
184        self.assertTrue(os.path.exists(image_path))
185        self.assertTrue(os.path.exists(image_path + ".sha256sum"))
186
187        # check if the resulting sha256sum agrees
188        self.assertTrue(runCmd('cd %s;sha256sum -c %s.%s.sha256sum' %
189                               (deploy_dir_image, link_name, conv)))
190
191    def test_image_fstypes(self):
192        """
193        Summary:     Check if image of supported image fstypes can be built
194        Expected:    core-image-minimal can be built for various image types
195        Product:     oe-core
196        Author:      Ed Bartosh <ed.bartosh@linux.intel.com>
197        """
198        image_name = 'core-image-minimal'
199
200        all_image_types = set(get_bb_var("IMAGE_TYPES", image_name).split())
201        blacklist = set(('container', 'elf', 'f2fs', 'multiubi', 'tar.zst', 'wic.zst'))
202        img_types = all_image_types - blacklist
203
204        config = 'IMAGE_FSTYPES += "%s"\n'\
205                 'MKUBIFS_ARGS ?= "-m 2048 -e 129024 -c 2047"\n'\
206                 'UBINIZE_ARGS ?= "-m 2048 -p 128KiB -s 512"' % ' '.join(img_types)
207        self.write_config(config)
208
209        bitbake(image_name)
210
211        deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
212        link_name = get_bb_var('IMAGE_LINK_NAME', image_name)
213        for itype in img_types:
214            image_path = os.path.join(deploy_dir_image, "%s.%s" % (link_name, itype))
215            # check if result image is in deploy directory
216            self.assertTrue(os.path.exists(image_path),
217                            "%s image %s doesn't exist" % (itype, image_path))
218
219    def test_useradd_static(self):
220        config = """
221USERADDEXTENSION = "useradd-staticids"
222USERADD_ERROR_DYNAMIC = "skip"
223USERADD_UID_TABLES += "files/static-passwd"
224USERADD_GID_TABLES += "files/static-group"
225"""
226        self.write_config(config)
227        bitbake("core-image-base")
228
229    def test_no_busybox_base_utils(self):
230        config = """
231# Enable wayland
232DISTRO_FEATURES:append += "pam opengl wayland"
233
234# Switch to systemd
235DISTRO_FEATURES += "systemd"
236VIRTUAL-RUNTIME_init_manager = "systemd"
237VIRTUAL-RUNTIME_initscripts = ""
238VIRTUAL-RUNTIME_syslog = ""
239VIRTUAL-RUNTIME_login_manager = "shadow-base"
240DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
241
242# Replace busybox
243PREFERRED_PROVIDER_virtual/base-utils = "packagegroup-core-base-utils"
244VIRTUAL-RUNTIME_base-utils = "packagegroup-core-base-utils"
245VIRTUAL-RUNTIME_base-utils-hwclock = "util-linux-hwclock"
246VIRTUAL-RUNTIME_base-utils-syslog = ""
247
248# Blacklist busybox
249PNBLACKLIST[busybox] = "Don't build this"
250"""
251        self.write_config(config)
252
253        bitbake("--graphviz core-image-weston")
254
255    def test_image_gen_debugfs(self):
256        """
257        Summary:     Check debugfs generation
258        Expected:    1. core-image-minimal can be build with IMAGE_GEN_DEBUGFS variable set
259                     2. debug filesystem is created when variable set
260                     3. debug symbols available
261        Product:     oe-core
262        Author:      Humberto Ibarra <humberto.ibarra.lopez@intel.com>
263                     Yeoh Ee Peng <ee.peng.yeoh@intel.com>
264        """
265
266        image_name = 'core-image-minimal'
267        features = 'IMAGE_GEN_DEBUGFS = "1"\n'
268        features += 'IMAGE_FSTYPES_DEBUGFS = "tar.bz2"\n'
269        features += 'MACHINE = "genericx86-64"\n'
270        self.write_config(features)
271
272        bitbake(image_name)
273        deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
274        dbg_tar_file = os.path.join(deploy_dir_image, "*-dbg.rootfs.tar.bz2")
275        debug_files = glob.glob(dbg_tar_file)
276        self.assertNotEqual(len(debug_files), 0, 'debug filesystem not generated at %s' % dbg_tar_file)
277        result = runCmd('cd %s; tar xvf %s' % (deploy_dir_image, dbg_tar_file))
278        self.assertEqual(result.status, 0, msg='Failed to extract %s: %s' % (dbg_tar_file, result.output))
279        result = runCmd('find %s -name %s' % (deploy_dir_image, "udevadm"))
280        self.assertTrue("udevadm" in result.output, msg='Failed to find udevadm: %s' % result.output)
281        dbg_symbols_targets = result.output.splitlines()
282        self.assertTrue(dbg_symbols_targets, msg='Failed to split udevadm: %s' % dbg_symbols_targets)
283        for t in dbg_symbols_targets:
284            result = runCmd('objdump --syms %s | grep debug' % t)
285            self.assertTrue("debug" in result.output, msg='Failed to find debug symbol: %s' % result.output)
286
287    def test_empty_image(self):
288        """Test creation of image with no packages"""
289        bitbake('test-empty-image')
290        res_dir = get_bb_var('DEPLOY_DIR_IMAGE')
291        images = os.path.join(res_dir, "test-empty-image-*.manifest")
292        result = glob.glob(images)
293        with open(result[1],"r") as f:
294                self.assertEqual(len(f.read().strip()),0)
295