From 8867a5572f7ad4138648a71b29a69f73af3ea966 Mon Sep 17 00:00:00 2001 From: Heitor Matsui Date: Fri, 31 Oct 2025 15:41:34 -0300 Subject: [PATCH] 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 --- software/scripts/major-release-upload | 168 ++---------------- software/scripts/upgrade_utils.py | 12 ++ .../software/api/controllers/v1/release.py | 2 +- software/software/software_controller.py | 2 +- 4 files changed, 33 insertions(+), 151 deletions(-) diff --git a/software/scripts/major-release-upload b/software/scripts/major-release-upload index cf982968..8a5fb7f7 100644 --- a/software/scripts/major-release-upload +++ b/software/scripts/major-release-upload @@ -40,21 +40,6 @@ FEED_REMOTE = "starlingx" FEED_BRANCH = "starlingx" -# TODO(bqian) move the function to shareable utils. -def get_major_release_version(sw_release_version): - """Gets the major release for a given software version """ - if not sw_release_version: - return None - else: - try: - separator = '.' - separated_string = sw_release_version.split(separator) - major_version = separated_string[0] + separator + separated_string[1] - return major_version - except Exception: - return None - - def setup_from_release_load(from_release, to_feed_dir): """ Setup from release load @@ -67,7 +52,7 @@ def setup_from_release_load(from_release, to_feed_dir): return try: - from_major_rel = get_major_release_version(from_release) + from_major_rel = upgrade_utils.get_major_release_version(from_release) # Copy install_uuid to /var/www/pages/feed/rel- from_feed_dir = os.path.join(FEED_OSTREE_BASE_DIR, ("rel-%s" % from_major_rel)) @@ -99,7 +84,7 @@ def setup_from_release_load(from_release, to_feed_dir): raise -def load_import(from_release, to_major_rel, iso_mount_dir): +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) @@ -107,14 +92,11 @@ def load_import(from_release, to_major_rel, iso_mount_dir): :param iso_mount_dir: iso mount dir """ - # for now the from_release is the same as from_major_rel. until - # the sw_version is redefied to major release version, there is - # chance that from_release could be major.minor.patch. - + 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- os.makedirs(FEED_OSTREE_BASE_DIR, exist_ok=True) - to_feed_dir = os.path.join(FEED_OSTREE_BASE_DIR, ("rel-%s" % to_major_rel)) if os.path.exists(to_feed_dir): shutil.rmtree(to_feed_dir) LOG.info("Removed existing %s", to_feed_dir) @@ -191,121 +173,30 @@ def load_import(from_release, to_major_rel, iso_mount_dir): def move_metadata_file_to_target_dir(to_release, iso_mount_dir, target_dir): """ - Move release metadata file to target dir in /opt/software/metadata/ + 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 metadata.xml to target dir in /opt/software/metadata/ + # Copy all *-metadata.xml to target_dir os.makedirs(target_dir, exist_ok=True) - metadata_name = f"{RELEASE_GA_NAME % to_release}-metadata.xml" - LOG.info("metadata name: %s", metadata_name) - abs_stx_release_metadata_file = os.path.join(iso_mount_dir, - 'patches', - metadata_name) - - # Copy stx release metadata.xml to available metadata dir - # TODO(jli14): prepatched iso will have more than one metadata file. - shutil.copyfile(abs_stx_release_metadata_file, - os.path.join(target_dir, metadata_name)) - LOG.info("Copied %s to %s", abs_stx_release_metadata_file, target_dir) + 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 release %s metadata file to %s" % - (to_release, target_dir)) + LOG.exception("Failed to copy the %s metadata files to %s" % (to_release, target_dir)) raise -def generate_metadata_file_in_unavailable_dir(to_release): - """ - Generate release metadata file in /opt/software/metadata/unavailable - This is only for 22.12 pre USM iso load import - :param to_release: release version - """ - try: - # Copy metadata.xml to /opt/software/metadata/unavailable - os.makedirs(UNAVAILABLE_DIR, exist_ok=True) - # TODO(jli14): release name should be dynamically generated based on the branch. - metadata_name = f"{RELEASE_GA_NAME % to_release}-metadata.xml" - LOG.info("metadata name: %s", metadata_name) - - # Generate metadata.xml - import xml.etree.ElementTree as ET - from xml.dom import minidom - - root = ET.Element('patch') - ET.SubElement(root, "id").text = RELEASE_GA_NAME % to_release - ET.SubElement(root, "sw_version").text = to_release - ET.SubElement(root, "component").text = RELEASE_GA_NAME.split('-')[0] - ET.SubElement(root, "summary").text = 'This file is generated by major-release-upload' - xml_str = ET.tostring(root, encoding='unicode') - pretty_xml = minidom.parseString(xml_str).toprettyxml(indent=" ") - pretty_xml = '\n'.join([line for line in pretty_xml.split('\n') if line.strip()]) - - # Write to file - abs_path_metadata_filename = os.path.join(UNAVAILABLE_DIR, metadata_name) - with open(abs_path_metadata_filename, "w") as file: - file.write(pretty_xml) - - except Exception: - LOG.exception("Failed to copy the release %s metadata file to %s" % - (to_release, UNAVAILABLE_DIR)) - raise - - -def copy_patch_metadata_files_to_committed(iso_mount_dir): - """ - Copy patch metadata files to /opt/software/metadata/committed - :param iso_mount_dir: iso mount dir - """ - committed_patch_dir = os.path.join(iso_mount_dir, 'patches') - try: - shutil.copytree(committed_patch_dir, COMMITTED_DIR, dirs_exist_ok=True) - LOG.info("Copied patch metadata file to %s", COMMITTED_DIR) - except shutil.Error: - LOG.exception("Failed to copy patch metadata file(s) to %s" % - COMMITTED_DIR) - raise - - -def copy_patch_metadata_files_to_patching_committed(iso_mount_dir): - # TODO(jli14): remove this function when 'sw-patch query' is deprecated - """ - Copy patch metadata files to /opt/patching/metadata/committed - :param iso_mount_dir: iso mount dir - """ - deployed_patch_dir = os.path.join(iso_mount_dir, 'patches') - try: - shutil.copytree(deployed_patch_dir, PATCHING_COMMITTED_DIR, dirs_exist_ok=True) - LOG.info("Copied patch metadata file to %s", PATCHING_COMMITTED_DIR) - except shutil.Error: - LOG.exception("Failed to copy patch metadata file(s) to %s" % - PATCHING_COMMITTED_DIR) - raise - - -def restart_legacy_patching_service(): - """ - Restart legacy patching service daemon - """ - # TODO(jli14): remove this function when 'sw-patch query' is deprecated - try: - restart_cmd = ['pmon-restart', 'sw-patch-controller-daemon'] - LOG.info("Restarting legacy patching service daemon: %s", " ".join(restart_cmd)) - subprocess.run(restart_cmd, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, check=True, text=True) - LOG.info("Restarted legacy patching service daemon successfully") - except Exception as e: - LOG.exception("Failed to restart legacy patching service daemon with error: %s", str(e)) - raise - - -def sync_inactive_load_to_controller1(to_major_rel): +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_major_rel: release version + :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", @@ -330,48 +221,27 @@ def main(): 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.", ) - - parser.add_argument( - "--is-usm-iso", - required=False, - help="True if the iso supports USM upgrade.", - default=True - ) - args = parser.parse_args() try: - to_major_rel = get_major_release_version(args.to_release) LOG.info("Load import from %s to %s started", args.from_release, args.to_release) - load_import(args.from_release, to_major_rel, args.iso_dir) + load_import(args.from_release, args.to_release, args.iso_dir) - if args.is_usm_iso in ["True", True]: # This is USM compatible iso - 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) - else: - move_metadata_file_to_target_dir(args.to_release, args.iso_dir, AVAILABLE_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: - # pre USM iso needs to generate metadata file - generate_metadata_file_in_unavailable_dir(args.to_release) - copy_patch_metadata_files_to_committed(args.iso_dir) - - # Currently imported load is N-1 - if args.from_release in ['None', None]: - copy_patch_metadata_files_to_patching_committed(args.iso_dir) - restart_legacy_patching_service() - sync_inactive_load_to_controller1(to_major_rel) + move_metadata_file_to_target_dir(args.to_release, args.iso_dir, AVAILABLE_DIR) except Exception as e: LOG.exception(e) diff --git a/software/scripts/upgrade_utils.py b/software/scripts/upgrade_utils.py index 841ead50..924e7a2b 100644 --- a/software/scripts/upgrade_utils.py +++ b/software/scripts/upgrade_utils.py @@ -11,6 +11,7 @@ import configparser import json import logging import os +from packaging import version import re import requests import subprocess @@ -290,3 +291,14 @@ def to_bool(value): if isinstance(value, str): return value.lower() == 'true' return False + + +def get_major_release_version(sw_release_version): + """Gets the major release for a given software version """ + if not sw_release_version: + return None + try: + v = version.Version(sw_release_version) + return f"{v.major:02d}.{v.minor:02d}" + except Exception: + return None diff --git a/software/software/api/controllers/v1/release.py b/software/software/api/controllers/v1/release.py index b41eeba6..62881f0d 100644 --- a/software/software/api/controllers/v1/release.py +++ b/software/software/api/controllers/v1/release.py @@ -93,7 +93,7 @@ class ReleaseController(RestController): LOG.info("Uploaded files: %s", uploaded_files) # Process uploaded files - return sc.software_release_upload(uploaded_files) + return sc.software_release_upload_api(uploaded_files) finally: # Remove all uploaded files from /scratch dir diff --git a/software/software/software_controller.py b/software/software/software_controller.py index 482e195e..ff2de77d 100644 --- a/software/software/software_controller.py +++ b/software/software/software_controller.py @@ -1953,7 +1953,7 @@ class PatchController(PatchService): return local_info, local_warning, local_error, upload_patch_info - def software_release_upload(self, release_files): + def software_release_upload_api(self, release_files): """ Upload software release files :return: dict of info, warning and error messages