update/software/scripts/prepare-chroot-mounts
Heitor Matsui f1a7ec2360 Improve deploy start upgrade scripts
This commit changes all deploy start upgrade scripts
to python, improve them by removing duplicated/needless
code and add more logging. The summary of the changes are:

- All shell scripts were converted to python code
- Some scripts were renamed to follow a naming convention
  and as a symlink with the old name was added to them for
  backwards compatibility
- Improved error logging and exception try/except blocks
- Removed from deploy start script the unnecessary initial
  step to initialize a temporary ostree_repo, changing it
  to checkout the ostree_repo directly from the feed

Test Plan:
PASS: AIO-SX stx-10 to stx-11 e2e upgrade
PASS: AIO-DX stx-10 to stx-11 e2e upgrade

Story: 2011357
Task: 51892

Change-Id: I63c324b169883c6e9007bc99fd67b5f5b2880668
Signed-off-by: Heitor Matsui <heitorvieira.matsui@windriver.com>
2025-04-03 17:15:31 -03:00

138 lines
4.8 KiB
Python

#!/usr/bin/python3
#
# Copyright (c) 2023-2025 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
#
# This script sets up bind mounts from the local filesystem
# to the checked out to-release ostree filesystem so that
# the data migration running under chroot has proper access
# to the local filesystem data and configuration.
#
import logging as LOG
import os
import subprocess
import sys
import upgrade_utils
class ChrootMounts:
DEV_PATH="/dev"
PLATFORM_PATH="/opt/platform"
RABBIT_PATH="/var/lib/rabbitmq"
POSTGRES_PATH="/var/lib/postgresql"
KUBERNETES_PATH="/var/lib/kubernetes"
PLATFORM_CONF_PATH="/etc/platform"
TMP_PATH="/tmp"
USR_PATH="/usr"
ETC_PATH="/etc"
PROC_PATH="/proc"
LOG_PATH="/var/log"
def __init__(self, checkout_dir):
self._mount_points = { # src:dst
os.path.normpath(self.DEV_PATH):
os.path.normpath(f"{checkout_dir}/{self.DEV_PATH}"),
os.path.normpath(self.PLATFORM_PATH):
os.path.normpath(f"{checkout_dir}/{self.PLATFORM_PATH}"),
os.path.normpath(self.RABBIT_PATH):
os.path.normpath(f"{checkout_dir}/{self.RABBIT_PATH}"),
os.path.normpath(self.POSTGRES_PATH):
os.path.normpath(f"{checkout_dir}/{self.POSTGRES_PATH}"),
os.path.normpath(self.KUBERNETES_PATH):
os.path.normpath(f"{checkout_dir}/{self.KUBERNETES_PATH}"),
os.path.normpath(self.PROC_PATH):
os.path.normpath(f"{checkout_dir}/{self.PROC_PATH}"),
os.path.normpath(self.LOG_PATH):
os.path.normpath(f"{checkout_dir}/{self.LOG_PATH}"),
os.path.normpath(self.PLATFORM_CONF_PATH):
os.path.normpath(f"{self.TMP_PATH}/{self.PLATFORM_CONF_PATH}"),
os.path.normpath(f"{checkout_dir}/{self.USR_PATH}/{self.ETC_PATH}"):
os.path.normpath(f"{checkout_dir}/{self.ETC_PATH}"),
}
def mount(self):
for src, dst in self._mount_points.items():
try:
os.makedirs(dst, exist_ok=True)
cmd = ["mount", "--bind", src, dst]
subprocess.run(cmd, check=True, text=True, capture_output=True)
except subprocess.CalledProcessError as e:
LOG.error(f"Failed to bind mount {src} to {dst}: {str(e.stderr)}")
self.umount()
raise
LOG.info(f"Bind mounted {src} -> {dst}")
def umount(self):
for _, dst in self._mount_points.items():
try:
subprocess.run(["umount", "-l", dst], check=True, text=True, capture_output=True)
except subprocess.CalledProcessError as e:
# ignore messages that are not harmful
if "not mounted" in e.stderr or "no mount point specified" in e.stderr:
continue
LOG.error(f"Failed to umount {dst}: {e.stderr}")
raise
LOG.info(f"Unmounted {dst}")
def check(self):
mounted = []
for _, dst in self._mount_points.items():
try:
cmd = ["findmnt", dst]
subprocess.run(cmd, check=True)
mounted.append(dst)
except subprocess.CalledProcessError:
pass
if len(mounted) > 0:
mounted_message = f"Mounted mount points: {', '.join(mounted)}"
print(mounted_message)
LOG.error(mounted_message)
raise OSError(mounted_message)
def run(self, operation):
try:
if operation == "-m":
self.mount()
elif operation == "-c":
self.check()
elif operation == "-u":
self.umount()
except Exception:
return 1
return 0
if __name__ == "__main__":
upgrade_utils.configure_logging("/var/log/software.log", log_level=LOG.INFO)
checkout_dir = None
operation = None
for arg in range(1, len(sys.argv)):
if arg == 1:
checkout_dir = sys.argv[arg]
elif arg == 2:
operation = sys.argv[arg]
if checkout_dir is None or operation is None:
usage_msg = (f"usage: {os.path.basename(sys.argv[0])} <ostree-checkout-dir> <-m|-c|-u>\n"
f"-m: bind mounts the local directories into checkout directories\n"
f"-c: check if there are existing bind mounts\n"
f"-u: unmount the bind mounts")
print(usage_msg)
LOG.error(usage_msg)
sys.exit(1)
if not os.path.isdir(checkout_dir):
error_msg = f"Invalid directory: {checkout_dir}"
print(error_msg)
LOG.error(error_msg)
sys.exit(1)
chroot_mounts = ChrootMounts(checkout_dir)
sys.exit(chroot_mounts.run(operation))