From: Alexander Heinisch <
alexander...@siemens.com>
Currently the installer deploys images embedded to itself.
In many situations this is fine, but for some more demanding requirements
are in place. e.g.:
- enrollment of keys (e.g. Secure Boot),
- setup for disk encryption,
- collection and reporting of device information (serial number, mac addresses, ...)
- resize / repartition of disk during comissioning
- generation of a commissioning report
While many of these tasks can be done on first-boot doing so during
device setup can be beneficial in many cases.
e.g.
- additional time required for "first-boot" when performing tasks
with high effort on site may not be desired
- the comissioning environment oftentimes allows for failures during
that phase - worst case the device is put aside. On site failures during
the wrong bootstrapping phases (secure boot setup, disk encryption)
can be severe.
This patch extends current installer behaviour to support a flexible
framework to define multiple tasks to be executed in sequence during
the "target (device) bootstrapping" phase.
Signed-off-by: Alexander Heinisch <
alexander...@siemens.com>
---
meta-isar/classes/target-bootstrapper.bbclass | 37 ++++++++++++++++
.../files/target-bootstrapper.override.conf | 3 ++
.../
target-bootstrapper-service.bb | 38 ++++++++++++++++
.../files/target-bootstrapper.sh.tmpl | 44 +++++++++++++++++++
.../
target-bootstrapper.bb | 41 +++++++++++++++++
5 files changed, 163 insertions(+)
create mode 100644 meta-isar/classes/target-bootstrapper.bbclass
create mode 100644 meta-isar/recipes-installer/target-bootstrapper-service/files/target-bootstrapper.override.conf
create mode 100644 meta-isar/recipes-installer/target-bootstrapper-service/
target-bootstrapper-service.bb
create mode 100644 meta-isar/recipes-installer/target-bootstrapper/files/target-bootstrapper.sh.tmpl
create mode 100644 meta-isar/recipes-installer/target-bootstrapper/
target-bootstrapper.bb
diff --git a/meta-isar/classes/target-bootstrapper.bbclass b/meta-isar/classes/target-bootstrapper.bbclass
new file mode 100644
index 00000000..15974bf8
--- /dev/null
+++ b/meta-isar/classes/target-bootstrapper.bbclass
@@ -0,0 +1,37 @@
+# This software is a part of ISAR.
+# Copyright (C) Siemens AG, 2024-2025
+#
+# SPDX-License-Identifier: MIT
+
+python() {
+ additional_packages = d.getVar('TARGET_BOOTSTRAPPER_ADDITIONAL_PACKAGES').split()
+
+ names = []
+ workdirs = []
+ scripts = []
+ efforts = []
+ effort_total = 0
+
+ for package in additional_packages:
+ additional_package_task = f"TARGET_BOOTSTRAPPER_TASK_{package}"
+
+ names.append(package)
+ workdirs.append(d.getVarFlag(additional_package_task, "workdir") or ".")
+
+ script = d.getVarFlag(additional_package_task, "script")
+ if not script:
+ bb.warn("Script not set for {task_name} - consider setting {task_name}[script] = \"<your-script-for-{task_name}>\"".format(task_name=additional_package_task))
+
+ scripts.append(script or "/bin/true")
+
+ effort = d.getVarFlag(additional_package_task, "effort") or "1"
+ efforts.append(effort)
+
+ effort_total = effort_total + int(effort)
+
+ d.setVar('TMPL_TARGET_BOOTSTRAPPER_TASK_NAMES', ' '.join(names))
+ d.setVar('TMPL_TARGET_BOOTSTRAPPER_TASK_WORKDIRS', ' '.join(workdirs))
+ d.setVar('TMPL_TARGET_BOOTSTRAPPER_TASK_SCRIPTS', ' '.join(scripts))
+ d.setVar('TMPL_TARGET_BOOTSTRAPPER_TASK_EFFORTS', ' '.join(efforts))
+ d.setVar('TMPL_TARGET_BOOTSTRAPPER_TASK_TOTAL_EFFORT', str(effort_total))
+}
diff --git a/meta-isar/recipes-installer/target-bootstrapper-service/files/target-bootstrapper.override.conf b/meta-isar/recipes-installer/target-bootstrapper-service/files/target-bootstrapper.override.conf
new file mode 100644
index 00000000..a7948418
--- /dev/null
+++ b/meta-isar/recipes-installer/target-bootstrapper-service/files/target-bootstrapper.override.conf
@@ -0,0 +1,3 @@
+[Service]
+ExecStart=
+ExecStart=/bin/sh -c "target-bootstrapper.sh || (echo 'Rebooting in 60 s'; sleep 60); reboot"
diff --git a/meta-isar/recipes-installer/target-bootstrapper-service/
target-bootstrapper-service.bb b/meta-isar/recipes-installer/target-bootstrapper-service/
target-bootstrapper-service.bb
new file mode 100644
index 00000000..c3441494
--- /dev/null
+++ b/meta-isar/recipes-installer/target-bootstrapper-service/
target-bootstrapper-service.bb
@@ -0,0 +1,38 @@
+# This software is a part of ISAR.
+# Copyright (C) Siemens AG, 2024-2025
+#
+# SPDX-License-Identifier: MIT
+
+DESCRIPTION = "systemd service to run target bootstrapper on ${TARGET_BOOTSTRAPPER_TTY_SERVICES}"
+
+TARGET_BOOTSTRAPPER_TTY_SERVICES ??= "\
+ getty@tty1 \
+ serial-getty@ttyS0 \
+ "
+
+python(){
+ if not d.getVar('TARGET_BOOTSTRAPPER_TTY_SERVICES'):
+ bb.error("No ttys for target bootstrapper configured - review TARGET_BOOTSTRAPPER_TTY_SERVICES setting")
+
+ if (bb.utils.to_boolean(d.getVar('INSTALLER_UNATTENDED')) and
+ len(d.getVar('TARGET_BOOTSTRAPPER_TTY_SERVICES').split()) != 1):
+ bb.warn("Multiple ttys are configured for target bootstrapper in unattended mode. - potential race condition detected!")
+}
+
+inherit dpkg-raw
+
+SRC_URI = "\
+ file://target-bootstrapper.override.conf \
+ "
+
+DEPENDS += " target-bootstrapper"
+DEBIAN_DEPENDS = "target-bootstrapper"
+
+do_install[cleandirs] = "${D}/usr/lib/systemd/system/"
+do_install() {
+ for svc_name in ${TARGET_BOOTSTRAPPER_TTY_SERVICES}
+ do
+ mkdir -p ${D}/usr/lib/systemd/system/${svc_name}.service.d/
+ install -m 0644 ${WORKDIR}/target-bootstrapper.override.conf ${D}/usr/lib/systemd/system/${svc_name}.service.d/10-target-bootstrapper.override.conf
+ done
+}
diff --git a/meta-isar/recipes-installer/target-bootstrapper/files/target-bootstrapper.sh.tmpl b/meta-isar/recipes-installer/target-bootstrapper/files/target-bootstrapper.sh.tmpl
new file mode 100644
index 00000000..13be023a
--- /dev/null
+++ b/meta-isar/recipes-installer/target-bootstrapper/files/target-bootstrapper.sh.tmpl
@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+# This software is a part of ISAR.
+# Copyright (C) Siemens AG, 2024
+#
+# SPDX-License-Identifier: MIT
+
+task_names=($TMPL_TARGET_BOOTSTRAPPER_TASK_NAMES)
+task_workdirs=($TMPL_TARGET_BOOTSTRAPPER_TASK_WORKDIRS)
+task_scripts=($TMPL_TARGET_BOOTSTRAPPER_TASK_SCRIPTS)
+task_efforts=($TMPL_TARGET_BOOTSTRAPPER_TASK_EFFORTS)
+handled_effort=0
+total_effort=$TMPL_TARGET_BOOTSTRAPPER_TASK_TOTAL_EFFORT
+
+tasks_total=${#task_names[@]}
+tasks_indices=${!task_names[@]}
+
+echo "Found $tasks_total tasks to execute for target bootstrapping."
+echo ""
+
+for idx in ${tasks_indices}
+do
+ echo "Task $(( idx+1 ))/${tasks_total} - $((handled_effort*100/total_effort))%"
+ echo "Handling task ${task_names[$idx]}"
+
+ echo "Entering workdir ${task_workdirs[$idx]}..."
+ pushd ${task_workdirs[$idx]} > /dev/null
+
+ ## execute task in subshell
+ (./${task_scripts[$idx]})
+ execution_result=$?
+ if [ ${execution_result} -eq 0 ]; then
+ echo "${task_names[$idx]} executed sucessfully"
+ else
+ echo "${task_names[$idx]} failed with ${execution_result}" -> abort!
+ exit ${execution_result}
+ fi
+
+ echo "Leaving workdir ${task_workdirs[$idx]}..."
+ popd > /dev/null
+
+ handled_effort=$((handled_effort+task_efforts[idx]))
+done
+
+echo "All tasks completed!"
diff --git a/meta-isar/recipes-installer/target-bootstrapper/
target-bootstrapper.bb b/meta-isar/recipes-installer/target-bootstrapper/
target-bootstrapper.bb
new file mode 100644
index 00000000..4c0fa6c3
--- /dev/null
+++ b/meta-isar/recipes-installer/target-bootstrapper/
target-bootstrapper.bb
@@ -0,0 +1,41 @@
+# This software is a part of ISAR.
+# Copyright (C) Siemens AG, 2024-2025
+#
+# SPDX-License-Identifier: MIT
+
+inherit dpkg-raw
+inherit target-bootstrapper
+
+DESCRIPTION = "Device bootstrapping framework"
+
+TARGET_BOOTSTRAPPER_ADDITIONAL_PACKAGES ??= "deploy-image"
+TARGET_BOOTSTRAPPER_TASK_deploy-image[script] ??= "deploy-image-wic.sh"
+TARGET_BOOTSTRAPPER_TASK_deploy-image[workdir] ??= "/usr/bin"
+TARGET_BOOTSTRAPPER_TASK_deploy-image[effort] ??= "2"
+
+DEPENDS += " ${@isar_multiarch_packages('TARGET_BOOTSTRAPPER_ADDITIONAL_PACKAGES', d)}"
+DEBIAN_DEPENDS += " \
+ , bash \
+ , ${@ ', '.join(isar_multiarch_packages('TARGET_BOOTSTRAPPER_ADDITIONAL_PACKAGES', d).split())} \
+ "
+
+SRC_URI = " \
+ file://target-bootstrapper.sh.tmpl \
+ "
+
+TEMPLATE_FILES = " \
+ target-bootstrapper.sh.tmpl \
+ "
+
+TEMPLATE_VARS = " \
+ TMPL_TARGET_BOOTSTRAPPER_TASK_NAMES \
+ TMPL_TARGET_BOOTSTRAPPER_TASK_WORKDIRS \
+ TMPL_TARGET_BOOTSTRAPPER_TASK_SCRIPTS \
+ TMPL_TARGET_BOOTSTRAPPER_TASK_EFFORTS \
+ TMPL_TARGET_BOOTSTRAPPER_TASK_TOTAL_EFFORT \
+ "
+
+do_install[cleandirs] = "${D}/usr/bin/"
+do_install() {
+ install -m 0755 ${WORKDIR}/target-bootstrapper.sh ${D}/usr/bin/
+}
--
2.39.5