From 51ed9820f78506286194b47e4b0470e98b643384 Mon Sep 17 00:00:00 2001 From: Lindley Vieira Date: Mon, 29 Dec 2025 17:32:47 -0500 Subject: [PATCH] Run post-install script before and after reboot This change runs the post-install script before and after the reboot for all reboot-required patches. And it passes an argument to the script indicating if it is running before or after. Also adds a pre-install and a post-install script boilerplate to make them standard across multiple patches. Test-Plan: PASS: Run a patch without a post-install script. PASS: (SX/DX) Run a RR patch with a post-install script running functions both before and after reboot. PASS: Run an inservice patch with a post-install script. PASS: A failure in the install script should fail the deploy. PASS: Run pre-bootstrap a patch with a post-install script. PASS: Remove a RR patch with a post-install script running functions both before and after reboot. Story: 2010676 Task: 53590 Change-Id: Ib80282de17d9160a792749adba4ddcbc19cd5c38 Signed-off-by: Lindley Vieira --- .../boilerplate/post-install.sh | 29 +++++++++++ .../boilerplate/pre-install.sh | 18 +++++++ software/service-files/run-software-scripts | 20 +++++--- software/service-files/software-functions | 18 +++++-- software/software/constants.py | 6 ++- software/software/software_agent.py | 50 +++++++++++-------- 6 files changed, 108 insertions(+), 33 deletions(-) create mode 100644 patch-scripts/install-scripts/boilerplate/post-install.sh create mode 100644 patch-scripts/install-scripts/boilerplate/pre-install.sh diff --git a/patch-scripts/install-scripts/boilerplate/post-install.sh b/patch-scripts/install-scripts/boilerplate/post-install.sh new file mode 100644 index 000000000..46ea30dbd --- /dev/null +++ b/patch-scripts/install-scripts/boilerplate/post-install.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# +# Copyright (c) 2026 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +# import common functions and constants +# file in update/software/service-files/software-functions +. /etc/software/software-functions + +running="$BEFORE_REBOOT" +if [ -n "$1" ]; then + running="$1" +fi + +loginfo "### Start of post-install script running $running ###" + +if [[ "$running" == "$BEFORE_REBOOT" ]]; then + loginfo "Running script before reboot (or in-service)" + # Put commands to run before reboot here +else + loginfo "Running script after reboot" + # Put commands to run after reboot here +fi + +loginfo "### End of post-install script ###" +exit $PATCH_STATUS_OK # in case of success +# exit $PATCH_STATUS_FAILED # in case of an error diff --git a/patch-scripts/install-scripts/boilerplate/pre-install.sh b/patch-scripts/install-scripts/boilerplate/pre-install.sh new file mode 100644 index 000000000..a442e7cb3 --- /dev/null +++ b/patch-scripts/install-scripts/boilerplate/pre-install.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (c) 2026 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +# import common functions and constants +# file in update/software/service-files/software-functions +. /etc/software/software-functions + +loginfo "### Start of pre-install script ###" + +# Put commands here + +loginfo "### End of pre-install script ###" +exit $PATCH_STATUS_OK # in case of success +# exit $PATCH_STATUS_FAILED # in case of an error diff --git a/software/service-files/run-software-scripts b/software/service-files/run-software-scripts index 0e9abafc5..1e04ddd47 100644 --- a/software/service-files/run-software-scripts +++ b/software/service-files/run-software-scripts @@ -1,35 +1,43 @@ #!/bin/bash # -# Copyright (c) 2023-2024 Wind River Systems, Inc. +# Copyright (c) 2023-2026 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # . /etc/software/software-functions +declare REBOOT_STATUS="" + if [ -z "$1" ] then loginfo "No input parameter provided to identify script type." exit 0 fi +if [ -n "$2" ] +then + REBOOT_STATUS="$2" + loginfo "Reboot status: $2" +fi + declare DIR="${PATCH_SCRIPTDIR}/${1}" -declare SCRIPTS=$(find $DIR -type f -executable | sort) +declare SCRIPTS=$(find $DIR -type f -executable | sort -V) if [ ${#SCRIPTS} -eq 0 ] then - loginfo "No in-service patch scripts found at the directory ${DIR}" + loginfo "No patch install script found at the directory ${DIR}" exit 0 fi declare -i NUM_SCRIPTS=$(echo "$SCRIPTS" | wc -l) -loginfo "Running $NUM_SCRIPTS in-service patch scripts" +loginfo "Running $NUM_SCRIPTS patch install scripts" declare SCRIPTLOG=/var/log/software.log cat <>$SCRIPTLOG ############################################################ -`date "+%FT%T.%3N"`: Running $NUM_SCRIPTS install patch scripts: +`date "+%FT%T.%3N"`: Running $NUM_SCRIPTS patch install scripts: $SCRIPTS @@ -45,7 +53,7 @@ do EOF - bash -x $cmd >>$SCRIPTLOG 2>&1 + bash -x $cmd "$REBOOT_STATUS" >>$SCRIPTLOG 2>&1 rc=$? if [ $rc -ne $PATCH_STATUS_OK ] then diff --git a/software/service-files/software-functions b/software/service-files/software-functions index 07bfc67bd..f6cc1ff7e 100644 --- a/software/service-files/software-functions +++ b/software/service-files/software-functions @@ -1,5 +1,5 @@ # -# Copyright (c) 2023 Wind River Systems, Inc. +# Copyright (c) 2023-2026 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -12,13 +12,16 @@ # Source platform.conf, for nodetype and subfunctions . /etc/platform/platform.conf -declare PATCH_SCRIPTDIR=/run/software/software-scripts -declare PRE_INSTALL_SCRIPTDIR=/run/software/software-scripts/preinstall -declare POST_INSTALL_SCRIPTDIR=/run/software/software-scripts/postinstall +declare PATCH_SCRIPTDIR=/var/persist/software-agent/software-scripts +declare PRE_INSTALL_SCRIPTDIR="${PATCH_SCRIPTDIR}/preinstall" +declare POST_INSTALL_SCRIPTDIR="${PATCH_SCRIPTDIR}/postinstall" declare PATCH_FLAGDIR=/run/software/software-flags declare -i PATCH_STATUS_OK=0 declare -i PATCH_STATUS_FAILED=1 +declare BEFORE_REBOOT="before-reboot" +declare AFTER_REBOOT="after-reboot" + declare logfile=/var/log/software.log declare NAME=$(basename $0) @@ -52,3 +55,10 @@ function is_locked() test -f /var/run/.node_locked } +not_controller() { + [[ "$nodetype" != "controller" ]] +} + +is_dc_central_controller() { + [[ "$nodetype" == "controller" && "$distributed_cloud_role" == "systemcontroller" ]] +} diff --git a/software/software/constants.py b/software/software/constants.py index b30e5ae53..f5472bfe8 100644 --- a/software/software/constants.py +++ b/software/software/constants.py @@ -1,5 +1,5 @@ """ -Copyright (c) 2023-2025 Wind River Systems, Inc. +Copyright (c) 2023-2026 Wind River Systems, Inc. SPDX-License-Identifier: Apache-2.0 @@ -271,3 +271,7 @@ READ_WRITE_PERMISSION = "rw,noatime" ETC = "/etc" USR = "/usr" USR_ETC = "/usr/etc" + +# Software-agent +BEFORE_REBOOT = "before-reboot" +AFTER_REBOOT = "after-reboot" diff --git a/software/software/software_agent.py b/software/software/software_agent.py index f6e89ec3e..096124a70 100644 --- a/software/software/software_agent.py +++ b/software/software/software_agent.py @@ -1,5 +1,5 @@ """ -Copyright (c) 2024-2025 Wind River Systems, Inc. +Copyright (c) 2024-2026 Wind River Systems, Inc. SPDX-License-Identifier: Apache-2.0 @@ -46,10 +46,11 @@ ostree_pull_completed_deployment_pending_file = \ "/var/run/ostree_pull_completed_deployment_pending" run_hooks_flag = "/var/run/run_hooks" mount_pending_file = "/var/run/mount_pending" -insvc_software_scripts = "/run/software/software-scripts" +install_scripts = "%s/software-scripts" % SOFTWARE_PERSIST_FOLDER insvc_software_flags = "/run/software/software-flags" insvc_software_restart_agent = "/run/software/.restart.software-agent" +# script located at update/software/service-files/ run_install_software_scripts_cmd = "/usr/sbin/run-software-scripts" pa = None @@ -74,6 +75,11 @@ def clearflag(fname): def pull_install_scripts_from_controller(install_local=False): + # Clear install scripts folder and use an empty one + if os.path.exists(install_scripts): + shutil.rmtree(install_scripts, ignore_errors=True) + os.makedirs(install_scripts, 0o700) + # If the rsync fails, it raises an exception to # the caller "handle_install()" and fails the # host-install request for this host. @@ -88,7 +94,7 @@ def pull_install_scripts_from_controller(install_local=False): "--delete", "--exclude", "tmp", "rsync://%s/repo/software-scripts/" % host, - "%s/" % insvc_software_scripts], + "%s/" % install_scripts], stderr=subprocess.STDOUT) LOG.info("Synced restart scripts from controller: %s", output) except subprocess.CalledProcessError as e: @@ -99,15 +105,21 @@ def pull_install_scripts_from_controller(install_local=False): raise -def run_post_install_script(): - LOG.info("Running post-install patch-scripts") +def run_post_install_script(running_after_reboot=False): + success = True + + running = constants.AFTER_REBOOT if running_after_reboot else constants.BEFORE_REBOOT + LOG.info(f"Running post-install patch-scripts {running}") try: - subprocess.check_output([run_install_software_scripts_cmd, "postinstall"], + subprocess.check_output([run_install_software_scripts_cmd, "postinstall", running], stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: LOG.exception("Failed to execute post-install scripts.") LOG.error("Command output: %s", e.output) + success = False + + return success def check_install_uuid(): @@ -673,13 +685,10 @@ class PatchAgent(PatchService): setflag(patch_installing_file) try: - # Create insvc patch directories - if not os.path.exists(insvc_software_scripts): - os.makedirs(insvc_software_scripts, 0o700) if not os.path.exists(insvc_software_flags): os.makedirs(insvc_software_flags, 0o700) except Exception: - LOG.exception("Failed to create in-service patch directories") + LOG.exception("Failed to create in-service flag") # Send a hello to provide a state update if self.sock_out is not None: @@ -773,9 +782,9 @@ class PatchAgent(PatchService): print("This node has been patched.") if os.path.exists(node_is_software_updated_rr_file): - LOG.info("Reboot is required. Skipping patch-scripts") + LOG.info("Reboot is required.") elif disallow_insvc_patch: - LOG.info("Disallowing patch-scripts. Treating as reboot-required") + LOG.info("Treating as reboot-required") setflag(node_is_software_updated_rr_file) else: LOG.info("Mounting the new deployment") @@ -787,21 +796,18 @@ class PatchAgent(PatchService): setflag(mount_pending_file) ostree_utils.mount_new_deployment(deployment_dir, active_dir) clearflag(mount_pending_file) - LOG.info("Running post-install patch-scripts") - subprocess.check_output([run_install_software_scripts_cmd, "postinstall"], - stderr=subprocess.STDOUT) # Clear the node_is_patched flag, since we've handled it in-service clearflag(node_is_patched_file) self.node_is_patched = False - except subprocess.CalledProcessError as e: - LOG.exception("Failed to execute post-install scripts.") - LOG.error("Command output: %s", e.output) + except OSTreeCommandFail: + LOG.exception("Failed to mount in-service deployment") success = False - # Clear the in-service patch dirs - if os.path.exists(insvc_software_scripts): - shutil.rmtree(insvc_software_scripts, ignore_errors=True) + if success: + LOG.info("Running post-install patch scripts") + success = run_post_install_script() + if os.path.exists(insvc_software_flags): shutil.rmtree(insvc_software_flags, ignore_errors=True) @@ -1062,7 +1068,7 @@ def main(): # Run on reboot after the node was updated by a reboot required patch/ISO if os.path.exists(node_is_software_updated_rr_file): ostree_utils.delete_older_deployments() - run_post_install_script() + run_post_install_script(running_after_reboot=True) clearflag(node_is_software_updated_rr_file) if len(sys.argv) <= 1: