Merge "Be able to run cloud-init when a seed iso is detected in virtual media"

This commit is contained in:
Zuul 2025-05-02 12:27:10 +00:00 committed by Gerrit Code Review
commit ba0efbfda6
5 changed files with 255 additions and 0 deletions

View File

@ -0,0 +1,35 @@
#!/bin/bash
#
# Copyright (c) 2025 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# cloud-init script to configure udev rule and service
# for seed iso
#
NOCLOUD=/opt/nocloud
echo "Configure the system to start cloud-init from a seed iso - start"
# Copy the udev rule and service to the appropriate locations.
# The udev rule will trigger the cloud-init-seed service
# when a block device with the label CIDATA or cidata is detected
# The cloud-init-seed service will run the script
# /var/lib/factory-install/run-cloud-init-from-seed.sh which internally
# runs cloud-init init and cloud-init modules.
# We are creating a custom cloud.cfg to prevent network validations
# during cloud-init init.
cp -f ${NOCLOUD}/seed-config/99-seediso.rules /etc/udev/rules.d/.
cp -f ${NOCLOUD}/seed-config/run-cloud-init-from-seed.sh /var/lib/factory-install/.
cp -f ${NOCLOUD}/seed-config/cloud-init-seed.service /etc/systemd/system/.
cp -f ${NOCLOUD}/seed-config/cloud.cfg /var/lib/factory-install/.
echo "Reloading udev rules"
udevadm control --reload-rules
udevadm trigger --subsystem-match=block
echo "Configure the system to start cloud-init from a seed iso - end"
exit 0

View File

@ -0,0 +1,24 @@
#
# Copyright (c) 2025 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# This udev rule triggers when a block device representing a
# CD/DVD drive (e.g., /dev/sr0, /dev/sr1, etc.) detects a
# media change (like a disc or an ISO being detected).
# Specifically, it applies under the following conditions:
#
# The device is part of the block subsystem.
# The kernel name matches sr*, which means it's a SCSI CD-ROM
# device.
# The ID_CDROM_MEDIA environment variable is set to "1",
# indicating that media (a disc) is present in the drive.
# The udev ACTION is "change", which typically happens when
# media is inserted or removed.
#
# When all of these conditions are met, the rule:
# Adds the systemd tag to the udev event (allowing systemd
# to process it).
# Instructs systemd to start the cloud-init-seed.service
# as a result of this event.
SUBSYSTEM=="block", KERNEL=="sr*", ENV{ID_CDROM_MEDIA}=="1", ACTION=="change", TAG+="systemd", ENV{SYSTEMD_WANTS}="cloud-init-seed.service"

View File

@ -0,0 +1,14 @@
[Unit]
Description=Cloud-init from seed ISO
After=local-fs.target
RequiresMountsFor=/opt/nocloud
[Service]
Type=oneshot
ExecStart=/var/lib/factory-install/run-cloud-init-from-seed.sh
RemainAfterExit=no
StandardOutput=append:/var/log/cloud-init.log
StandardError=append:/var/log/cloud-init.log
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,50 @@
#
# Copyright (c) 2025 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# The top level settings are used as module
# and system configuration.
# This is a customized version of the default cloud.cfg file to
# prevent cloud-init from trying to do any network validation.
network:
config: disabled
# A set of users which may be applied and/or used by various modules
# when a 'default' entry is found it will reference the 'default_user'
# from the distro configuration specified below
users:
- default
# This will cause the set+update hostname module to not operate (if true)
preserve_hostname: false
# This prevents cloud-init from rewriting apt's sources.list file,
# which has been a source of surprise.
apt_preserve_sources_list: true
# The modules that run in the 'init' stage
cloud_init_modules: []
# The modules that run in the 'config' stage
cloud_config_modules:
# Emit the cloud config ready event
# this can be used by upstart jobs for 'start on cloud-config'.
- emit_upstart
- runcmd
# The modules that run in the 'final' stage
cloud_final_modules: []
# System and/or distro specific settings
# (not accessible to handlers/transforms)
system_info:
# This will affect which distro class gets used
distro: debian
# Other config here will be given to the distro class and/or path classes
paths:
cloud_dir: /var/lib/cloud/
templates_dir: /etc/cloud/templates/
upstart_dir: /etc/init/
ssh_svcname: ssh

View File

@ -0,0 +1,132 @@
#!/bin/bash
#
# Copyright (c) 2025 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# cloud-init script to run cloud-init from a seed ISO
#
SCRIPT_PATH=$(realpath "$0")
LOCK_FILE="/run/cloud-init-seediso.lock"
ORIGIN_CLOUD_CFG="/etc/cloud/cloud.cfg"
CUSTOM_CLOUD_CFG="/var/lib/factory-install/cloud.cfg"
FACTORY_INSTALL_COMPLETE_FILE="/var/lib/factory-install/complete"
SEED_UDEV_RULES="/etc/udev/rules.d/99-seediso.rules"
SEED_SERVICE="/etc/systemd/system/cloud-init-seed.service"
function check_rc_die {
local -i rc=${1}
msg=${2}
if [ ${rc} -ne 0 ]; then
log_fatal "${msg} [rc=${rc}]"
fi
}
function log_fatal {
echo "$(date +"%Y-%m-%d %H:%M:%S,%3N - cloud-init-seed -") FATAL: ${*}"
exit 1
}
function log_warn {
echo "$(date +"%Y-%m-%d %H:%M:%S,%3N - cloud-init-seed -") WARN: ${*}"
}
function log_info {
echo "$(date +"%Y-%m-%d %H:%M:%S,%3N - cloud-init-seed -") INFO: $*"
}
function restore_cloud_init_config {
# Restore the original cloud.cfg file from the backup.
if [[ -f "$ORIGIN_CLOUD_CFG.bak" ]]; then
mv -f "$ORIGIN_CLOUD_CFG.bak" "$ORIGIN_CLOUD_CFG"
else
log_warn "Original cloud.cfg backup not found. Skipping restore."
fi
}
# Lock file to prevent multiple instances of the script from running
# simultaneously. The lock file is created in the /run directory.
# The lock file is used to ensure that only one instance of the script
# is running at a time. If another instance of the script is already
# running, the script will exit without doing anything.
exec 200>"$LOCK_FILE"
flock -n 200 || {
log_warn "Another instance of the script is already running. Exiting."
exit 0
}
# If clean is passed as an argument, remove the udev rule and service,
# the custom cloud.cfg file, and the script itself.
# This is to ensure that the cloud-init-seed service is not triggered
# again after the script has been run successfully.
if [[ "$1" == "clean" ]]; then
rm -f $SEED_UDEV_RULES
rm -f $SEED_SERVICE
rm -f $CUSTOM_CLOUD_CFG
rm -f $SCRIPT_PATH
udevadm control --reload-rules
systemctl daemon-reexec
exit 0
fi
log_info "Starting cloud-init using seed ISO..."
# Checks if factory-install has been completed. This is required to be able
# to run cloud-init from a seed ISO.
if [[ ! -f "$FACTORY_INSTALL_COMPLETE_FILE" ]]; then
log_fatal "Cloud-init from factory-install has not been completed yet. Exiting."
fi
# Finds the first device found with the label CIDATA or cidata.
# If the device is not found, exit the script.
DEVICE=$(blkid -L "cidata" 2>/dev/null || blkid -L "CIDATA" 2>/dev/null | head -1)
if [[ -z "$DEVICE" ]]; then
log_fatal "No ISO with label 'CIDATA' found. Exiting."
fi
# Checks if the device is cloud-init compatible by checking
# if the user-data and meta-data files exist in the ISO.
# If they do not exist, exit the script.
if ! isoinfo -i "$DEVICE" -l 2>/dev/null | grep -qE "user-data|meta-data"; then
log_fatal "ISO $DEVICE is not cloud-init compatible: missing user-data or meta-data."
fi
# Check if the custom cloud.cfg file exists.
# If it does not exist, exit the script.
if [[ ! -f "$CUSTOM_CLOUD_CFG" ]]; then
log_fatal "Custom cloud.cfg file not found. Exiting."
fi
# Backup the original cloud.cfg file to prevent
# network validation during cloud-init init.
# The original cloud.cfg file is backed up to a file with the same name
# and a .bak extension.
if [[ ! -f "$ORIGIN_CLOUD_CFG" ]]; then
log_fatal "Original cloud.cfg file not found. Exiting."
fi
cp -f "$ORIGIN_CLOUD_CFG" "$ORIGIN_CLOUD_CFG".bak
check_rc_die $? "Unable to backup the cloud.cfg file"
# Replace the original cloud.cfg file with the custom cloud.cfg file.
# The custom cloud.cfg file is used to prevent network validation
# during cloud-init init.
cp -f "$CUSTOM_CLOUD_CFG" "$ORIGIN_CLOUD_CFG"
check_rc_die $? "Unable to copy factory-install cloud.cfg file"
# Runs cloud-init commands to apply seed iso configuration.
# The commands are run in the following order:
# 1. cloud-init clean: Cleans up cloud-init state.
# 2. cloud-init init: Initializes cloud-init.
# 3. cloud-init modules --mode=config: Runs the config modules.
# 4. cloud-init modules --mode=final: Runs the final modules.
cloud-init clean &&
cloud-init init &&
cloud-init modules --mode=config &&
cloud-init modules --mode=final
CLOUD_INIT_RC=$?
restore_cloud_init_config
check_rc_die $CLOUD_INIT_RC "cloud-init failed to run from seed ISO."
log_info "cloud-init from seed ISO completed successfully."
exit 0