xref: /OK3568_Linux_fs/yocto/poky/meta/lib/oeqa/selftest/cases/overlayfs.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1#
2# SPDX-License-Identifier: MIT
3#
4
5from oeqa.selftest.case import OESelftestTestCase
6from oeqa.utils.commands import bitbake, runqemu
7from oeqa.core.decorator import OETestTag
8
9def getline_qemu(out, line):
10    for l in out.split('\n'):
11        if line in l:
12            return l
13
14def getline(res, line):
15    return getline_qemu(res.output, line)
16
17class OverlayFSTests(OESelftestTestCase):
18    """Overlayfs class usage tests"""
19
20    def add_overlay_conf_to_machine(self):
21        machine_inc = """
22OVERLAYFS_MOUNT_POINT[mnt-overlay] = "/mnt/overlay"
23"""
24        self.set_machine_config(machine_inc)
25
26    def test_distro_features_missing(self):
27        """
28        Summary:   Check that required DISTRO_FEATURES are set
29        Expected:  Fail when either systemd or overlayfs are not in DISTRO_FEATURES
30        Author:    Vyacheslav Yurkov <uvv.mail@gmail.com>
31        """
32
33        config = """
34IMAGE_INSTALL:append = " overlayfs-user"
35"""
36        overlayfs_recipe_append = """
37inherit overlayfs
38"""
39        self.write_config(config)
40        self.add_overlay_conf_to_machine()
41        self.write_recipeinc('overlayfs-user', overlayfs_recipe_append)
42
43        res = bitbake('core-image-minimal', ignore_status=True)
44        line = getline(res, "overlayfs-user was skipped: missing required distro features")
45        self.assertTrue("overlayfs" in res.output, msg=res.output)
46        self.assertTrue("systemd" in res.output, msg=res.output)
47        self.assertTrue("ERROR: Required build target 'core-image-minimal' has no buildable providers." in res.output, msg=res.output)
48
49    def test_not_all_units_installed(self):
50        """
51        Summary:   Test QA check that we have required mount units in the image
52        Expected:  Fail because mount unit for overlay partition is not installed
53        Author:    Vyacheslav Yurkov <uvv.mail@gmail.com>
54        """
55
56        config = """
57IMAGE_INSTALL:append = " overlayfs-user"
58DISTRO_FEATURES:append = " systemd overlayfs"
59"""
60
61        self.write_config(config)
62        self.add_overlay_conf_to_machine()
63
64        res = bitbake('core-image-minimal', ignore_status=True)
65        line = getline(res, " Mount path /mnt/overlay not found in fstab and unit mnt-overlay.mount not found in systemd unit directories")
66        self.assertTrue(line and line.startswith("WARNING:"), msg=res.output)
67        line = getline(res, "Not all mount paths and units are installed in the image")
68        self.assertTrue(line and line.startswith("ERROR:"), msg=res.output)
69
70    def test_not_all_units_installed_but_qa_skipped(self):
71        """
72        Summary:   Test skipping the QA check
73        Expected:  Image is created successfully
74        Author:    Claudius Heine <ch@denx.de>
75        """
76
77        config = """
78IMAGE_INSTALL:append = " overlayfs-user"
79DISTRO_FEATURES += "systemd overlayfs"
80OVERLAYFS_QA_SKIP[mnt-overlay] = "mount-configured"
81"""
82
83        self.write_config(config)
84        self.add_overlay_conf_to_machine()
85
86        bitbake('core-image-minimal')
87
88    def test_mount_unit_not_set(self):
89        """
90        Summary:   Test whether mount unit was set properly
91        Expected:  Fail because mount unit was not set
92        Author:    Vyacheslav Yurkov <uvv.mail@gmail.com>
93        """
94
95        config = """
96IMAGE_INSTALL:append = " overlayfs-user"
97DISTRO_FEATURES:append = " systemd overlayfs"
98"""
99
100        self.write_config(config)
101
102        res = bitbake('core-image-minimal', ignore_status=True)
103        line = getline(res, "A recipe uses overlayfs class but there is no OVERLAYFS_MOUNT_POINT set in your MACHINE configuration")
104        self.assertTrue(line and line.startswith("Parsing recipes...ERROR:"), msg=res.output)
105
106    def test_wrong_mount_unit_set(self):
107        """
108        Summary:   Test whether mount unit was set properly
109        Expected:  Fail because not the correct flag used for mount unit
110        Author:    Vyacheslav Yurkov <uvv.mail@gmail.com>
111        """
112
113        config = """
114IMAGE_INSTALL:append = " overlayfs-user"
115DISTRO_FEATURES:append = " systemd overlayfs"
116"""
117
118        wrong_machine_config = """
119OVERLAYFS_MOUNT_POINT[usr-share-overlay] = "/usr/share/overlay"
120"""
121
122        self.write_config(config)
123        self.set_machine_config(wrong_machine_config)
124
125        res = bitbake('core-image-minimal', ignore_status=True)
126        line = getline(res, "Missing required mount point for OVERLAYFS_MOUNT_POINT[mnt-overlay] in your MACHINE configuration")
127        self.assertTrue(line and line.startswith("Parsing recipes...ERROR:"), msg=res.output)
128
129    def _test_correct_image(self, recipe, data):
130        """
131        Summary:   Check that we can create an image when all parameters are
132                   set correctly
133        Expected:  Image is created successfully
134        Author:    Vyacheslav Yurkov <uvv.mail@gmail.com>
135        """
136
137        config = """
138IMAGE_INSTALL:append = " overlayfs-user systemd-machine-units"
139DISTRO_FEATURES:append = " systemd overlayfs"
140
141# Use systemd as init manager
142VIRTUAL-RUNTIME_init_manager = "systemd"
143
144# enable overlayfs in the kernel
145KERNEL_EXTRA_FEATURES:append = " features/overlayfs/overlayfs.scc"
146"""
147
148        overlayfs_recipe_append = """
149OVERLAYFS_WRITABLE_PATHS[mnt-overlay] += "/usr/share/another-overlay-mount"
150
151SYSTEMD_SERVICE:${PN} += " \
152    my-application.service \
153"
154
155do_install:append() {
156    install -d ${D}${systemd_system_unitdir}
157    cat <<EOT > ${D}${systemd_system_unitdir}/my-application.service
158[Unit]
159Description=Sample application start-up unit
160After=overlayfs-user-overlays.service
161Requires=overlayfs-user-overlays.service
162
163[Service]
164Type=oneshot
165ExecStart=/bin/true
166RemainAfterExit=true
167
168[Install]
169WantedBy=multi-user.target
170EOT
171}
172"""
173
174        self.write_config(config)
175        self.add_overlay_conf_to_machine()
176        self.write_recipeinc(recipe, data)
177        self.write_recipeinc('overlayfs-user', overlayfs_recipe_append)
178
179        bitbake('core-image-minimal')
180
181        with runqemu('core-image-minimal') as qemu:
182            # Check that application service started
183            status, output = qemu.run_serial("systemctl status my-application")
184            self.assertTrue("active (exited)" in output, msg=output)
185
186            # Check that overlay mounts are dependencies of our application unit
187            status, output = qemu.run_serial("systemctl list-dependencies my-application")
188            self.assertTrue("overlayfs-user-overlays.service" in output, msg=output)
189
190            status, output = qemu.run_serial("systemctl list-dependencies overlayfs-user-overlays")
191            self.assertTrue("usr-share-another\\x2doverlay\\x2dmount.mount" in output, msg=output)
192            self.assertTrue("usr-share-my\\x2dapplication.mount" in output, msg=output)
193
194            # Check that we have /mnt/overlay fs mounted as tmpfs and
195            # /usr/share/my-application as an overlay (see overlayfs-user recipe)
196            status, output = qemu.run_serial("/bin/mount -t tmpfs,overlay")
197
198            line = getline_qemu(output, "on /mnt/overlay")
199            self.assertTrue(line and line.startswith("tmpfs"), msg=output)
200
201            line = getline_qemu(output, "upperdir=/mnt/overlay/upper/usr/share/my-application")
202            self.assertTrue(line and line.startswith("overlay"), msg=output)
203
204            line = getline_qemu(output, "upperdir=/mnt/overlay/upper/usr/share/another-overlay-mount")
205            self.assertTrue(line and line.startswith("overlay"), msg=output)
206
207    @OETestTag("runqemu")
208    def test_correct_image_fstab(self):
209        """
210        Summary:   Check that we can create an image when all parameters are
211                   set correctly via fstab
212        Expected:  Image is created successfully
213        Author:    Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com>
214        """
215
216        base_files_append = """
217do_install:append() {
218    cat <<EOT >> ${D}${sysconfdir}/fstab
219tmpfs                /mnt/overlay         tmpfs      mode=1777,strictatime,nosuid,nodev  0  0
220EOT
221}
222"""
223
224        self._test_correct_image('base-files', base_files_append)
225
226    @OETestTag("runqemu")
227    def test_correct_image_unit(self):
228        """
229        Summary:   Check that we can create an image when all parameters are
230                   set correctly via mount unit
231        Expected:  Image is created successfully
232        Author:    Vyacheslav Yurkov <uvv.mail@gmail.com>
233        """
234
235        systemd_machine_unit_append = """
236SYSTEMD_SERVICE:${PN} += " \
237    mnt-overlay.mount \
238"
239
240do_install:append() {
241    install -d ${D}${systemd_system_unitdir}
242    cat <<EOT > ${D}${systemd_system_unitdir}/mnt-overlay.mount
243[Unit]
244Description=Tmpfs directory
245DefaultDependencies=no
246
247[Mount]
248What=tmpfs
249Where=/mnt/overlay
250Type=tmpfs
251Options=mode=1777,strictatime,nosuid,nodev
252
253[Install]
254WantedBy=multi-user.target
255EOT
256}
257
258"""
259
260        self._test_correct_image('systemd-machine-units', systemd_machine_unit_append)
261
262@OETestTag("runqemu")
263class OverlayFSEtcRunTimeTests(OESelftestTestCase):
264    """overlayfs-etc class tests"""
265
266    def test_all_required_variables_set(self):
267        """
268        Summary:   Check that required variables are set
269        Expected:  Fail when any of required variables is missing
270        Author:    Vyacheslav Yurkov <uvv.mail@gmail.com>
271        """
272
273        configBase = """
274DISTRO_FEATURES:append = " systemd"
275
276# Use systemd as init manager
277VIRTUAL-RUNTIME_init_manager = "systemd"
278
279# enable overlayfs in the kernel
280KERNEL_EXTRA_FEATURES:append = " features/overlayfs/overlayfs.scc"
281
282# Image configuration for overlayfs-etc
283EXTRA_IMAGE_FEATURES += "overlayfs-etc"
284IMAGE_FEATURES:remove = "package-management"
285"""
286        configMountPoint = """
287OVERLAYFS_ETC_MOUNT_POINT = "/data"
288"""
289        configDevice = """
290OVERLAYFS_ETC_DEVICE = "/dev/mmcblk0p1"
291"""
292
293        self.write_config(configBase)
294        res = bitbake('core-image-minimal', ignore_status=True)
295        line = getline(res, "OVERLAYFS_ETC_MOUNT_POINT must be set in your MACHINE configuration")
296        self.assertTrue(line, msg=res.output)
297
298        self.append_config(configMountPoint)
299        res = bitbake('core-image-minimal', ignore_status=True)
300        line = getline(res, "OVERLAYFS_ETC_DEVICE must be set in your MACHINE configuration")
301        self.assertTrue(line, msg=res.output)
302
303        self.append_config(configDevice)
304        res = bitbake('core-image-minimal', ignore_status=True)
305        line = getline(res, "OVERLAYFS_ETC_FSTYPE should contain a valid file system type on /dev/mmcblk0p1")
306        self.assertTrue(line, msg=res.output)
307
308    def test_image_feature_conflict(self):
309        """
310        Summary:   Overlayfs-etc is not allowed to be used with package-management
311        Expected:  Feature conflict
312        Author:    Vyacheslav Yurkov <uvv.mail@gmail.com>
313        """
314
315        config = """
316DISTRO_FEATURES:append = " systemd"
317
318# Use systemd as init manager
319VIRTUAL-RUNTIME_init_manager = "systemd"
320
321# enable overlayfs in the kernel
322KERNEL_EXTRA_FEATURES:append = " features/overlayfs/overlayfs.scc"
323EXTRA_IMAGE_FEATURES += "overlayfs-etc"
324EXTRA_IMAGE_FEATURES += "package-management"
325"""
326
327        self.write_config(config)
328
329        res = bitbake('core-image-minimal', ignore_status=True)
330        line = getline(res, "contains conflicting IMAGE_FEATURES")
331        self.assertTrue("overlayfs-etc" in res.output, msg=res.output)
332        self.assertTrue("package-management" in res.output, msg=res.output)
333
334    def test_image_feature_is_missing_class_included(self):
335        configAppend = """
336INHERIT += "overlayfs-etc"
337"""
338        self.run_check_image_feature(configAppend)
339
340    def test_image_feature_is_missing(self):
341        self.run_check_image_feature()
342
343    def run_check_image_feature(self, appendToConfig=""):
344        """
345        Summary:   Overlayfs-etc class is not applied when image feature is not set
346                   even if we inherit it directly,
347        Expected:  Image is created successfully but /etc is not an overlay
348        Author:    Vyacheslav Yurkov <uvv.mail@gmail.com>
349        """
350
351        config = f"""
352DISTRO_FEATURES:append = " systemd"
353
354# Use systemd as init manager
355VIRTUAL-RUNTIME_init_manager = "systemd"
356
357# enable overlayfs in the kernel
358KERNEL_EXTRA_FEATURES:append = " features/overlayfs/overlayfs.scc"
359
360IMAGE_FSTYPES += "wic"
361WKS_FILE = "overlayfs_etc.wks.in"
362
363EXTRA_IMAGE_FEATURES += "read-only-rootfs"
364# Image configuration for overlayfs-etc
365OVERLAYFS_ETC_MOUNT_POINT = "/data"
366OVERLAYFS_ETC_DEVICE = "/dev/sda3"
367{appendToConfig}
368"""
369
370        self.write_config(config)
371
372        bitbake('core-image-minimal')
373
374        with runqemu('core-image-minimal', image_fstype='wic') as qemu:
375            status, output = qemu.run_serial("/bin/mount")
376
377            line = getline_qemu(output, "upperdir=/data/overlay-etc/upper")
378            self.assertFalse(line, msg=output)
379
380    def test_sbin_init_preinit(self):
381        self.run_sbin_init(False)
382
383    def test_sbin_init_original(self):
384        self.run_sbin_init(True)
385
386    def run_sbin_init(self, origInit):
387        """
388        Summary:   Confirm we can replace original init and mount overlay on top of /etc
389        Expected:  Image is created successfully and /etc is mounted as an overlay
390        Author:    Vyacheslav Yurkov <uvv.mail@gmail.com>
391        """
392
393        config = """
394DISTRO_FEATURES:append = " systemd"
395
396# Use systemd as init manager
397VIRTUAL-RUNTIME_init_manager = "systemd"
398
399# enable overlayfs in the kernel
400KERNEL_EXTRA_FEATURES:append = " features/overlayfs/overlayfs.scc"
401
402IMAGE_FSTYPES += "wic"
403OVERLAYFS_INIT_OPTION = "{OVERLAYFS_INIT_OPTION}"
404WKS_FILE = "overlayfs_etc.wks.in"
405
406EXTRA_IMAGE_FEATURES += "read-only-rootfs"
407# Image configuration for overlayfs-etc
408EXTRA_IMAGE_FEATURES += "overlayfs-etc"
409IMAGE_FEATURES:remove = "package-management"
410OVERLAYFS_ETC_MOUNT_POINT = "/data"
411OVERLAYFS_ETC_FSTYPE = "ext4"
412OVERLAYFS_ETC_DEVICE = "/dev/sda3"
413OVERLAYFS_ETC_USE_ORIG_INIT_NAME = "{OVERLAYFS_ETC_USE_ORIG_INIT_NAME}"
414"""
415
416        args = {
417            'OVERLAYFS_INIT_OPTION': "" if origInit else "init=/sbin/preinit",
418            'OVERLAYFS_ETC_USE_ORIG_INIT_NAME': int(origInit == True)
419        }
420
421        self.write_config(config.format(**args))
422
423        bitbake('core-image-minimal')
424        testFile = "/etc/my-test-data"
425
426        with runqemu('core-image-minimal', image_fstype='wic', discard_writes=False) as qemu:
427            status, output = qemu.run_serial("/bin/mount")
428
429            line = getline_qemu(output, "/dev/sda3")
430            self.assertTrue("/data" in output, msg=output)
431
432            line = getline_qemu(output, "upperdir=/data/overlay-etc/upper")
433            self.assertTrue(line and line.startswith("/data/overlay-etc/upper on /etc type overlay"), msg=output)
434
435            status, output = qemu.run_serial("touch " + testFile)
436            status, output = qemu.run_serial("sync")
437            status, output = qemu.run_serial("ls -1 " + testFile)
438            line = getline_qemu(output, testFile)
439            self.assertTrue(line and line.startswith(testFile), msg=output)
440
441        # Check that file exists in /etc after reboot
442        with runqemu('core-image-minimal', image_fstype='wic') as qemu:
443            status, output = qemu.run_serial("ls -1 " + testFile)
444            line = getline_qemu(output, testFile)
445            self.assertTrue(line and line.startswith(testFile), msg=output)
446