Files
update/software/scripts/major-release-upload
Heitor Matsui 8867a5572f Major release upload cleanup and improvements
This commit focuses on USM major release upload:
- Removal of deprecated code from previous releases
- Implementation of pending TODO action items
- Change of release upload API function to align with
  the naming convention used by other endpoints

Test Plan
PASS: stx-12 major release upload
PASS: stx-11 major release upload in stx-12 system
      (upload inactive load for DC use case)

Story: 2011357
Task: 53027

Change-Id: Icd0e8f485f26577b8e00276313af601706d550d7
Signed-off-by: Heitor Matsui <heitorvieira.matsui@windriver.com>
2025-10-31 16:37:17 -03:00

254 lines
9.9 KiB
Python

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright (c) 2023-2024 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
"""
This script is run during 'software upload' command.
It is used to copy the required files from uploaded iso image
to the controller.
"""
import argparse
import configparser
import glob
import logging
import os
import shutil
import subprocess
import sys
import upgrade_utils
LOG = logging.getLogger('main_logger')
AVAILABLE_DIR = "/opt/software/metadata/available"
UNAVAILABLE_DIR = "/opt/software/metadata/unavailable"
COMMITTED_DIR = "/opt/software/metadata/committed"
PATCHING_COMMITTED_DIR = "/opt/patching/metadata/committed"
FEED_OSTREE_BASE_DIR = "/var/www/pages/feed"
RELEASE_GA_NAME = "starlingx-%s"
SOFTWARE_STORAGE_DIR = "/opt/software"
TMP_DIR = "/tmp"
VAR_PXEBOOT_DIR = "/var/pxeboot"
FEED_REMOTE = "starlingx"
FEED_BRANCH = "starlingx"
def setup_from_release_load(from_release, to_feed_dir):
"""
Setup from release load
:param from_release: from release version
:param to_feed_dir: to release feed directory
"""
# 'None' is passed from this script argument
if from_release == 'None':
LOG.info("From release is not specified. Skipping from release load")
return
try:
from_major_rel = upgrade_utils.get_major_release_version(from_release)
# Copy install_uuid to /var/www/pages/feed/rel-<release>
from_feed_dir = os.path.join(FEED_OSTREE_BASE_DIR, ("rel-%s" % from_major_rel))
shutil.copyfile(os.path.join(from_feed_dir, "install_uuid"),
os.path.join(to_feed_dir, "install_uuid"))
LOG.info("Copied install_uuid to %s", to_feed_dir)
# Copy pxeboot-update-${from_major_release}.sh to from-release feed /upgrades
from_iso_upgrades_dir = os.path.join(from_feed_dir, "upgrades")
os.makedirs(from_iso_upgrades_dir, exist_ok=True)
shutil.copyfile(os.path.join("/etc", "pxeboot-update-%s.sh" % from_major_rel),
os.path.join(from_iso_upgrades_dir, "pxeboot-update-%s.sh" % from_major_rel))
LOG.info("Copied pxeboot-update-%s.sh to %s", from_major_rel, from_iso_upgrades_dir)
# Copy pxelinux.cfg.files to from-release feed /pxeboot
from_feed_pxeboot_dir = os.path.join(from_feed_dir, "pxeboot")
os.makedirs(from_feed_pxeboot_dir, exist_ok=True)
# Find from-release pxelinux.cfg.files
pxe_dir = os.path.join(VAR_PXEBOOT_DIR, "pxelinux.cfg.files")
from_pxe_files = glob.glob(os.path.join(pxe_dir, '*' + from_major_rel))
for from_pxe_file in from_pxe_files:
if os.path.isfile(from_pxe_file):
shutil.copyfile(from_pxe_file, os.path.join(from_feed_pxeboot_dir,
os.path.basename(from_pxe_file)))
LOG.info("Copied %s to %s", from_pxe_file, from_feed_pxeboot_dir)
except Exception:
raise
def load_import(from_release, to_release, iso_mount_dir):
"""
Import the iso files to the feed and pxeboot directories
:param from_release: from release version (MM.mm/MM.mm.p)
:param to_release: to release version (MM.mm.p)
:param iso_mount_dir: iso mount dir
"""
to_major_rel = upgrade_utils.get_major_release_version(to_release)
to_feed_dir = os.path.join(FEED_OSTREE_BASE_DIR, ("rel-%s" % to_major_rel))
try:
# Copy the iso file to /var/www/pages/feed/rel-<release>
os.makedirs(FEED_OSTREE_BASE_DIR, exist_ok=True)
if os.path.exists(to_feed_dir):
shutil.rmtree(to_feed_dir)
LOG.info("Removed existing %s", to_feed_dir)
os.makedirs(to_feed_dir, exist_ok=True)
feed_contents = ["install_uuid", "efi.img", "kickstart",
"ostree_repo", "pxeboot", "upgrades"]
for content in feed_contents:
src_abs_path = os.path.join(iso_mount_dir, content)
if os.path.isfile(src_abs_path):
shutil.copyfile(src_abs_path, os.path.join(to_feed_dir, content))
LOG.info("Copied %s to %s", src_abs_path, to_feed_dir)
elif os.path.isdir(src_abs_path):
shutil.copytree(src_abs_path, os.path.join(to_feed_dir, content,), symlinks=True)
LOG.info("Copied %s to %s", src_abs_path, to_feed_dir)
# Add min-free-space-percent to feed ostree config file
config_path = os.path.join(to_feed_dir, "ostree_repo/config")
if os.path.exists(config_path):
config = configparser.ConfigParser()
config.read(config_path)
config.set("core", "min-free-space-percent", "0")
with open(config_path, 'w') as file:
config.write(file, space_around_delimiters=False)
# Create 'starlingx' remote on the feed ostree_repo
cmd = ["ostree", "remote", "add", "--no-gpg-verify",
"--repo=%s/ostree_repo/" % to_feed_dir, FEED_REMOTE,
"http://controller:8080/feed/rel-%s/ostree_repo/" % to_major_rel,
FEED_BRANCH]
try:
subprocess.check_call(cmd)
LOG.info("Created feed remote '%s'" % FEED_REMOTE)
except subprocess.CalledProcessError as e:
LOG.exception("Feed remote '%s' creation failed. Error: %s" % (FEED_REMOTE, str(e)))
raise
# Converted from upgrade package extraction script
shutil.copyfile(os.path.join(to_feed_dir, "kickstart", "kickstart.cfg"),
os.path.join(to_feed_dir, "kickstart.cfg"))
# Copy bzImage and initrd
bzimage_files = glob.glob(os.path.join(to_feed_dir, 'pxeboot', 'bzImage*'))
for bzimage_file in bzimage_files:
if os.path.isfile(bzimage_file):
shutil.copyfile(bzimage_file, os.path.join(VAR_PXEBOOT_DIR,
os.path.basename(bzimage_file)))
LOG.info("Copied %s to %s", bzimage_file, VAR_PXEBOOT_DIR)
initrd_files = glob.glob(os.path.join(to_feed_dir, 'pxeboot', 'initrd*'))
for initrd_file in initrd_files:
if os.path.isfile(initrd_file):
shutil.copyfile(initrd_file, os.path.join(VAR_PXEBOOT_DIR,
os.path.basename(initrd_file)))
LOG.info("Copied %s to %s", initrd_file, VAR_PXEBOOT_DIR)
# Copy pxeboot-update.sh to /etc
pxeboot_update_filename = "pxeboot-update-%s.sh" % to_major_rel
shutil.copyfile(os.path.join(to_feed_dir, "upgrades", pxeboot_update_filename),
os.path.join("/etc", pxeboot_update_filename))
os.chmod(os.path.join("/etc", pxeboot_update_filename), mode=0o755)
LOG.info("Copied pxeboot-update-%s.sh to %s", to_major_rel, "/etc")
# Setup from release load
setup_from_release_load(from_release, to_feed_dir)
except Exception as e:
LOG.exception("Load import failed. Error: %s" % str(e))
shutil.rmtree(to_feed_dir)
LOG.info("Removed %s", to_feed_dir)
raise
def move_metadata_file_to_target_dir(to_release, iso_mount_dir, target_dir):
"""
Copy release metadata file to target_dir
:param to_release: release version
:param iso_mount_dir: iso mount dir
:param target_dir: target directory the metadata file moves to
"""
try:
# Copy all *-metadata.xml to target_dir
os.makedirs(target_dir, exist_ok=True)
for metadata_file in glob.glob(os.path.join(iso_mount_dir, "patches", "*-metadata.xml")):
metadata_name = os.path.basename(metadata_file)
shutil.copyfile(metadata_file, os.path.join(target_dir, metadata_name))
LOG.info("Copied %s to %s", metadata_name, target_dir)
except shutil.Error:
LOG.exception("Failed to copy the %s metadata files to %s" % (to_release, target_dir))
raise
def sync_inactive_load_standby_controller(to_release):
"""
Sync inactive load to controller-1
Upload is only allowed in controller-0 so sync to controller-1 is needed
:param to_release: release version
"""
to_major_rel = upgrade_utils.get_major_release_version(to_release)
feed_dir = os.path.join(FEED_OSTREE_BASE_DIR, ("rel-%s" % to_major_rel))
sync_cmd = [
"rsync",
"-ac",
"--delete",
"--exclude", "tmp",
feed_dir,
"rsync://controller-1/feed"]
LOG.info("Syncing inactive load to controllers %s", ' '.join(sync_cmd))
subprocess.run(sync_cmd, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, check=True, text=True)
LOG.info("Sync controllers completed")
def main():
parser = argparse.ArgumentParser(
description="Import files from uploaded iso image to controller.",
epilog="Use %(prog)s -h for help.",
)
parser.add_argument(
"--from-release",
required=True,
help="The from-release version.",
)
parser.add_argument(
"--to-release",
required=True,
help="The to-release version, MM.mm.p",
)
parser.add_argument(
"--iso-dir",
required=True,
help="The mounted iso image directory.",
)
args = parser.parse_args()
try:
LOG.info("Load import from %s to %s started", args.from_release, args.to_release)
load_import(args.from_release, args.to_release, args.iso_dir)
if args.from_release in ['None', None]: # This is N-1 load
move_metadata_file_to_target_dir(args.to_release, args.iso_dir, UNAVAILABLE_DIR)
sync_inactive_load_standby_controller(args.to_release)
else:
move_metadata_file_to_target_dir(args.to_release, args.iso_dir, AVAILABLE_DIR)
except Exception as e:
LOG.exception(e)
return 1
if __name__ == "__main__":
upgrade_utils.configure_logging('/var/log/software.log', log_level=logging.INFO)
sys.exit(main())