Support install scripts for in-service patch
This commit adds support for 1) Post-install scripts for In-Service patches 2) Pre-install scripts for In-Service patches Test Plan: [PENDING] Install scripts run before and after install [PENDING] Scripts are optional Story: 2010676 Task: 49480 Change-Id: I6729798a59ac61c7eae395d8a3a3b425fe4e6f72 Signed-off-by: Jessica Castelino <jessica.castelino@windriver.com> Co-Signed-off-by: sshathee <shunmugam.shatheesh@windriver.com> Signed-off-by: sshathee <shunmugam.shatheesh@windriver.com>
This commit is contained in:
parent
c5a7d1d336
commit
727714b39d
|
@ -1,13 +1,20 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2023 Wind River Systems, Inc.
|
||||
# Copyright (c) 2023-2024 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
. /etc/software/software-functions
|
||||
|
||||
declare SCRIPTS=$(find $PATCH_SCRIPTDIR -type f -executable | sort)
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
loginfo "No input parameter provided to identify script type."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
declare DIR="${PATCH_SCRIPTDIR}/${1}"
|
||||
declare SCRIPTS=$(find $DIR -type f -executable | sort)
|
||||
declare -i NUM_SCRIPTS=$(echo "$SCRIPTS" | wc -l)
|
||||
|
||||
if [ $NUM_SCRIPTS -eq 0 ]
|
||||
|
@ -18,10 +25,10 @@ fi
|
|||
|
||||
loginfo "Running $NUM_SCRIPTS in-service patch scripts"
|
||||
|
||||
declare SCRIPTLOG=/var/log/software-insvc.log
|
||||
declare SCRIPTLOG=/var/log/software-script.log
|
||||
cat <<EOF >>$SCRIPTLOG
|
||||
############################################################
|
||||
`date "+%FT%T.%3N"`: Running $NUM_SCRIPTS in-service patch scripts:
|
||||
`date "+%FT%T.%3N"`: Running $NUM_SCRIPTS install patch scripts:
|
||||
|
||||
$SCRIPTS
|
||||
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
# 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_FLAGDIR=/run/software/software-flags
|
||||
declare -i PATCH_STATUS_OK=0
|
||||
declare -i PATCH_STATUS_FAILED=1
|
||||
|
|
|
@ -45,7 +45,7 @@ insvc_software_scripts = "/run/software/software-scripts"
|
|||
insvc_software_flags = "/run/software/software-flags"
|
||||
insvc_software_restart_agent = "/run/software/.restart.software-agent"
|
||||
|
||||
run_insvc_software_scripts_cmd = "/usr/sbin/run-software-scripts"
|
||||
run_install_software_scripts_cmd = "/usr/sbin/run-software-scripts"
|
||||
|
||||
pa = None
|
||||
|
||||
|
@ -68,7 +68,7 @@ def clearflag(fname):
|
|||
LOG.exception("Failed to clear %s flag", fname)
|
||||
|
||||
|
||||
def pull_restart_scripts_from_controller():
|
||||
def pull_install_scripts_from_controller():
|
||||
# If the rsync fails, it raises an exception to
|
||||
# the caller "handle_install()" and fails the
|
||||
# host-install request for this host.
|
||||
|
@ -576,6 +576,11 @@ class PatchAgent(PatchService):
|
|||
os.path.exists(ostree_pull_completed_deployment_pending_file) or \
|
||||
os.path.exists(mount_pending_file):
|
||||
try:
|
||||
LOG.info("Running pre-install patch-scripts")
|
||||
pull_install_scripts_from_controller()
|
||||
subprocess.check_output([run_install_software_scripts_cmd, "preinstall"],
|
||||
stderr=subprocess.STDOUT)
|
||||
|
||||
# Pull changes from remote to the sysroot ostree
|
||||
# The remote value is configured inside
|
||||
# "/sysroot/ostree/repo/config" file
|
||||
|
@ -585,6 +590,10 @@ class PatchAgent(PatchService):
|
|||
LOG.exception("Failed to pull changes and create deployment"
|
||||
"during host-install.")
|
||||
success = False
|
||||
except subprocess.CalledProcessError as e:
|
||||
LOG.exception("Failed to execute pre-install scripts.")
|
||||
LOG.error("Command output: %s", e.output)
|
||||
success = False
|
||||
|
||||
try:
|
||||
# Create a new deployment once the changes are pulled
|
||||
|
@ -619,15 +628,15 @@ class PatchAgent(PatchService):
|
|||
setflag(mount_pending_file)
|
||||
ostree_utils.mount_new_deployment(deployment_dir)
|
||||
clearflag(mount_pending_file)
|
||||
LOG.info("Running in-service patch-scripts")
|
||||
pull_restart_scripts_from_controller()
|
||||
subprocess.check_output(run_insvc_software_scripts_cmd, stderr=subprocess.STDOUT)
|
||||
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("In-Service patch installation failed")
|
||||
LOG.exception("Failed to execute post-install scripts.")
|
||||
LOG.error("Command output: %s", e.output)
|
||||
success = False
|
||||
|
||||
|
|
|
@ -1075,22 +1075,27 @@ class PatchController(PatchService):
|
|||
ostree_tar_filename = "%s/%s-software.tar" % (ostree_tar_dir, patch_id)
|
||||
return ostree_tar_filename
|
||||
|
||||
def delete_restart_script(self, patch_id):
|
||||
def delete_install_script(self, patch_id):
|
||||
'''
|
||||
Deletes the restart script (if any) associated with the patch
|
||||
Deletes the install scripts associated with the patch
|
||||
:param patch_id: The patch ID
|
||||
'''
|
||||
release = self.release_collection.get_release_by_id(patch_id)
|
||||
restart_script = release.restart_script
|
||||
if not restart_script:
|
||||
return
|
||||
|
||||
restart_script_path = "%s/%s" % (root_scripts_dir, restart_script)
|
||||
pre_install_filename = self.release_data.metadata[patch_id].get("pre_install")
|
||||
post_install_filename = self.release_data.metadata[patch_id].get("post_install")
|
||||
pre_install_path = None
|
||||
post_install_path = None
|
||||
if pre_install_filename:
|
||||
pre_install_path = "%s/%s" % (root_scripts_dir, pre_install_filename)
|
||||
if post_install_filename:
|
||||
post_install_path = "%s/%s" % (root_scripts_dir, post_install_filename)
|
||||
try:
|
||||
# Delete the metadata
|
||||
os.remove(restart_script_path)
|
||||
# Delete the install scripts
|
||||
if pre_install_path:
|
||||
os.remove(pre_install_path)
|
||||
if post_install_path:
|
||||
os.remove(post_install_path)
|
||||
except OSError:
|
||||
msg = "Failed to remove restart script for %s" % patch_id
|
||||
msg = "Failed to remove the install script for %s" % patch_id
|
||||
LOG.exception(msg)
|
||||
raise SoftwareError(msg)
|
||||
|
||||
|
@ -1625,7 +1630,7 @@ class PatchController(PatchService):
|
|||
LOG.exception(msg)
|
||||
raise MetadataFail(msg)
|
||||
|
||||
self.delete_restart_script(release_id)
|
||||
self.delete_install_script(release_id)
|
||||
reload_release_data()
|
||||
msg = "%s has been deleted" % release_id
|
||||
LOG.info(msg)
|
||||
|
@ -1994,17 +1999,23 @@ class PatchController(PatchService):
|
|||
results["error"] += errormsg
|
||||
return results
|
||||
|
||||
|
||||
for patch_id in commit_list:
|
||||
release = self.release_collection.get_release_by_id(patch_id)
|
||||
# Fetch file paths that need to be cleaned up to
|
||||
# free patch storage disk space
|
||||
if release.restart_script:
|
||||
restart_script_path = "%s/%s" % \
|
||||
(root_scripts_dir,
|
||||
release.restart_script)
|
||||
if os.path.exists(restart_script_path):
|
||||
cleanup_files.add(restart_script_path)
|
||||
patch_sw_version = release.sw_release
|
||||
pre_install_filename = self.release_data.metadata[patch_id].get("pre_install")
|
||||
post_install_filename = self.release_data.metadata[patch_id].get("post_install")
|
||||
|
||||
if pre_install_filename:
|
||||
pre_install_script_path = "%s/%s" % (root_scripts_dir, pre_install_filename)
|
||||
post_install_script_path = "%s/%s" % (root_scripts_dir, post_install_filename)
|
||||
if os.path.exists(pre_install_script_path):
|
||||
cleanup_files.add(pre_install_script_path)
|
||||
if os.path.exists(post_install_script_path):
|
||||
cleanup_files.add(post_install_script_path)
|
||||
|
||||
patch_sw_version = utils.get_major_release_version(
|
||||
self.release_data.metadata[patch_id]["sw_version"])
|
||||
abs_ostree_tar_dir = package_dir[patch_sw_version]
|
||||
software_tar_path = "%s/%s-software.tar" % (abs_ostree_tar_dir, patch_id)
|
||||
if os.path.exists(software_tar_path):
|
||||
|
@ -2076,40 +2087,39 @@ class PatchController(PatchService):
|
|||
break
|
||||
return rc
|
||||
|
||||
def copy_restart_scripts(self):
|
||||
def copy_install_scripts(self):
|
||||
applying_states = [states.DEPLOYING, states.REMOVING]
|
||||
for release in self.release_collection.iterate_releases():
|
||||
if release.restart_script:
|
||||
if release.state in applying_states:
|
||||
try:
|
||||
restart_script_name = release.restart_script
|
||||
restart_script_path = "%s/%s" \
|
||||
% (root_scripts_dir, restart_script_name)
|
||||
dest_path = constants.PATCH_SCRIPTS_STAGING_DIR
|
||||
dest_script_file = "%s/%s" \
|
||||
% (constants.PATCH_SCRIPTS_STAGING_DIR, restart_script_name)
|
||||
for patch_id in self.release_data.metadata:
|
||||
pre_install = self.release_data.metadata[patch_id].get("pre_install")
|
||||
post_install = self.release_data.metadata[patch_id].get("post_install")
|
||||
folder = ["preinstall", "postinstall"]
|
||||
if self.release_data.metadata[patch_id]["state"] in applying_states:
|
||||
try:
|
||||
for i, file in enumerate(file for file in (pre_install, post_install) if file):
|
||||
script_path = "%s/%s" % (root_scripts_dir, file)
|
||||
dest_path = constants.PATCH_SCRIPTS_STAGING_DIR + "/" + folder[i]
|
||||
dest_script_file = "%s/%s" % (dest_path, file)
|
||||
if not os.path.exists(dest_path):
|
||||
os.makedirs(dest_path, 0o700)
|
||||
shutil.copyfile(restart_script_path, dest_script_file)
|
||||
shutil.copyfile(script_path, dest_script_file)
|
||||
os.chmod(dest_script_file, 0o700)
|
||||
msg = "Creating restart script for %s" % release.id
|
||||
msg = "Creating install script %s for %s" % (file, patch_id)
|
||||
LOG.info(msg)
|
||||
except shutil.Error:
|
||||
msg = "Failed to copy the restart script for %s" % release.id
|
||||
LOG.exception(msg)
|
||||
raise SoftwareError(msg)
|
||||
else:
|
||||
try:
|
||||
restart_script_name = release.restart_script
|
||||
restart_script_path = "%s/%s" \
|
||||
% (constants.PATCH_SCRIPTS_STAGING_DIR, restart_script_name)
|
||||
if os.path.exists(restart_script_path):
|
||||
os.remove(restart_script_path)
|
||||
msg = "Removing restart script for %s" % release.id
|
||||
LOG.info(msg)
|
||||
except shutil.Error:
|
||||
msg = "Failed to delete the restart script for %s" % release.id
|
||||
LOG.exception(msg)
|
||||
except shutil.Error:
|
||||
msg = "Failed to copy the install script %s for %s" % (file, patch_id)
|
||||
LOG.exception(msg)
|
||||
raise SoftwareError(msg)
|
||||
else:
|
||||
try:
|
||||
for i, file in enumerate(file for file in (pre_install, post_install) if file):
|
||||
script_path = "%s/%s/%s" % (constants.PATCH_SCRIPTS_STAGING_DIR, folder[i], file)
|
||||
if os.path.exists(script_path):
|
||||
os.remove(script_path)
|
||||
msg = "Removing install script %s for %s" % (file, patch_id)
|
||||
LOG.info(msg)
|
||||
except shutil.Error:
|
||||
msg = "Failed to delete the install script %s for %s" % (file, patch_id)
|
||||
LOG.exception(msg)
|
||||
|
||||
def _update_state_to_peer(self):
|
||||
state_update_msg = SoftwareMessageDeployStateUpdate()
|
||||
|
@ -2731,7 +2741,7 @@ class PatchController(PatchService):
|
|||
if self.allow_insvc_patching:
|
||||
LOG.info("Allowing in-service patching")
|
||||
force = True
|
||||
self.copy_restart_scripts()
|
||||
self.copy_install_scripts()
|
||||
|
||||
# Check if there is a major release deployment in progress
|
||||
# and set agent request parameters accordingly
|
||||
|
|
|
@ -347,7 +347,8 @@ class ReleaseData(object):
|
|||
"summary",
|
||||
"description",
|
||||
"install_instructions",
|
||||
"restart_script",
|
||||
"pre_install",
|
||||
"post_install",
|
||||
"warnings",
|
||||
"apply_active_release_only",
|
||||
"commit"]:
|
||||
|
@ -880,13 +881,16 @@ class PatchFile(object):
|
|||
v = "%s/%s-software.tar" % (abs_ostree_tar_dir, patch_id)
|
||||
LOG.info("software.tar %s" % v)
|
||||
|
||||
# restart_script may not exist in metadata.
|
||||
if thispatch.metadata[patch_id].get("restart_script"):
|
||||
if not os.path.exists(root_scripts_dir):
|
||||
os.makedirs(root_scripts_dir)
|
||||
restart_script_name = os.path.join(tmpdir, thispatch.metadata[patch_id]["restart_script"])
|
||||
if os.path.isfile(restart_script_name):
|
||||
shutil.move(restart_script_name, os.path.join(root_scripts_dir, restart_script_name))
|
||||
if not os.path.exists(root_scripts_dir):
|
||||
os.makedirs(root_scripts_dir)
|
||||
if thispatch.metadata[patch_id].get("pre_install"):
|
||||
pre_install_script_name = thispatch.metadata[patch_id]["pre_install"]
|
||||
shutil.move(pre_install_script_name,
|
||||
"%s/%s" % (root_scripts_dir, pre_install_script_name))
|
||||
if thispatch.metadata[patch_id].get("post_install"):
|
||||
post_install_script_name = thispatch.metadata[patch_id]["post_install"]
|
||||
shutil.move(post_install_script_name,
|
||||
"%s/%s" % (root_scripts_dir, post_install_script_name))
|
||||
|
||||
except tarfile.TarError as te:
|
||||
msg = "Extract software failed %s" % str(te)
|
||||
|
|
|
@ -52,7 +52,8 @@ metadata2 = """<?xml version="1.0" ?>
|
|||
<status>DEV</status>
|
||||
<unremovable>N</unremovable>
|
||||
<reboot_required>N</reboot_required>
|
||||
<restart_script>23.09_NRR_INSVC_example-cgcs-patch-restart</restart_script>
|
||||
<pre_install>pre-install.sh</pre_install>
|
||||
<post_install>post-install.sh</post_install>
|
||||
<contents>
|
||||
<ostree>
|
||||
<number_of_commits>1</number_of_commits>
|
||||
|
@ -81,7 +82,8 @@ expected_values = [
|
|||
"warnings": "Sample warning2",
|
||||
"status": "DEV",
|
||||
"unremovable": "N",
|
||||
"restart_script": "23.09_NRR_INSVC_example-cgcs-patch-restart",
|
||||
"pre_install": "pre-install.sh",
|
||||
"post_install": "post-install.sh",
|
||||
"commit_id": "0b53576092a189133d56eac49ae858c1218f480a4a859eaca2b47f2604a4e0e7",
|
||||
"checksum": "2f742b1b719f19b302c306604659ccf4aa61a1fdb7742ac79b009c79af18c79b",
|
||||
},
|
||||
|
@ -95,7 +97,8 @@ expected_values = [
|
|||
"warnings": "Sample warning",
|
||||
"status": "TST",
|
||||
"unremovable": "Y",
|
||||
"restart_script": None,
|
||||
"pre_install": "pre-install.sh",
|
||||
"post_install": "post-install.sh",
|
||||
"commit_id": "38453dcb1aeb5bb9394ed02c0e6b8f2f913d00a827c89faf98cb63dff503b8e2",
|
||||
"checksum": "2f742b1b719f19b302c306604659ccf4aa61a1fdb7742ac79b009c79af18c79b",
|
||||
}
|
||||
|
@ -137,10 +140,10 @@ class TestSoftwareFunction(unittest.TestCase):
|
|||
self.assertEqual(val["warnings"], r.warnings)
|
||||
self.assertEqual(val["status"], r.status)
|
||||
self.assertEqual(val["unremovable"] == 'Y', r.unremovable)
|
||||
if val["restart_script"] is None:
|
||||
self.assertIsNone(r.restart_script)
|
||||
if val["pre_install"] is None:
|
||||
self.assertIsNone(r.pre_install)
|
||||
else:
|
||||
self.assertEqual(val["restart_script"], r.restart_script)
|
||||
self.assertEqual(val["pre_install"], r.pre_install)
|
||||
self.assertEqual(val["commit_id"], r.commit_id)
|
||||
self.assertEqual(val["checksum"], r.commit_checksum)
|
||||
|
||||
|
@ -166,10 +169,10 @@ class TestSoftwareFunction(unittest.TestCase):
|
|||
self.assertEqual(val["warnings"], r.warnings)
|
||||
self.assertEqual(val["status"], r.status)
|
||||
self.assertEqual(val["unremovable"] == 'Y', r.unremovable)
|
||||
if val["restart_script"] is None:
|
||||
self.assertIsNone(r.restart_script)
|
||||
if val["pre_install"] is None:
|
||||
self.assertIsNone(r.pre_install)
|
||||
else:
|
||||
self.assertEqual(val["restart_script"], r.restart_script)
|
||||
self.assertEqual(val["pre_install"], r.pre_install)
|
||||
self.assertEqual(val["commit_id"], r.commit_id)
|
||||
self.assertEqual(val["checksum"], r.commit_checksum)
|
||||
|
||||
|
@ -185,10 +188,10 @@ class TestSoftwareFunction(unittest.TestCase):
|
|||
self.assertEqual(val["warnings"], r.warnings)
|
||||
self.assertEqual(val["status"], r.status)
|
||||
self.assertEqual(val["unremovable"] == 'Y', r.unremovable)
|
||||
if val["restart_script"] is None:
|
||||
self.assertIsNone(r.restart_script)
|
||||
if val["pre_install"] is None:
|
||||
self.assertIsNone(r.pre_install)
|
||||
else:
|
||||
self.assertEqual(val["restart_script"], r.restart_script)
|
||||
self.assertEqual(val["pre_install"], r.pre_install)
|
||||
self.assertEqual(val["commit_id"], r.commit_id)
|
||||
self.assertEqual(val["checksum"], r.commit_checksum)
|
||||
|
||||
|
|
Loading…
Reference in New Issue