diff --git a/playbookconfig/src/playbooks/install.yml b/playbookconfig/src/playbooks/install.yml index 1bef74006..40a19136f 100644 --- a/playbookconfig/src/playbooks/install.yml +++ b/playbookconfig/src/playbooks/install.yml @@ -119,4 +119,5 @@ check_bootstrap_address: false check_patches: false sync_patch_metadata: true + sync_software_metadata: true enforce_password_change: true diff --git a/playbookconfig/src/playbooks/prestage_images.yml b/playbookconfig/src/playbooks/prestage_images.yml index 3d831d9bc..0ccd7c8ed 100644 --- a/playbookconfig/src/playbooks/prestage_images.yml +++ b/playbookconfig/src/playbooks/prestage_images.yml @@ -11,13 +11,13 @@ # # Usage: # ansible-playbook prestage_images.yml -i \ -# -e "software_version= image_list_file=" +# -e "software_version= software_major_release= image_list_file=" # # Images file is mandatory if the specified software version is greater # than the current software version of the subcloud. - hosts: all - gather_facts: no + gather_facts: false vars: prestage_type: "images" @@ -28,4 +28,4 @@ roles: - prestage/prepare-env - - { role: prestage/prestage-images, become: yes } + - { role: prestage/prestage-images, become: true } diff --git a/playbookconfig/src/playbooks/prestage_sw_packages.yml b/playbookconfig/src/playbooks/prestage_sw_packages.yml index 4bc72c997..04206d0f0 100644 --- a/playbookconfig/src/playbooks/prestage_sw_packages.yml +++ b/playbookconfig/src/playbooks/prestage_sw_packages.yml @@ -13,29 +13,35 @@ # # Usage: # ansible-playbook prestage_sw_packages.yml -i \ -# -e "software_version=" +# -e "software_version= software_major_release= prestage_install=" - hosts: all - gather_facts: no + gather_facts: false vars: prestage_type: "packages" + prestage_install: for_install platform_backup_dir: /opt/platform-backup roles: - role: prestage/prepare-env + - role: prestage/sync-software-metadata + when: prestage_install == 'for_sw_deploy' and prestage_sync_software_metadata_required + vars: + ansible_become_pass: "{{ ansible_ssh_pass }}" + - role: prestage/prestage-sw-packages - when: prestage_os_type == 'debian' - become: yes + when: prestage_install == 'for_install' and prestage_os_type == 'debian' + become: true - role: prestage/prestage-sw-packages-centos - when: prestage_os_type == 'centos' - become: yes + when: prestage_install == 'for_install' and prestage_os_type == 'centos' + become: true - role: prestage/prestage-patches - when: host_patches_exist - become: yes + when: software_major_release is version('24.09', '<') and prestage_install == 'for_install' and host_patches_exist + become: true - role: prestage/get-prestage-versions - become: yes + become: true diff --git a/playbookconfig/src/playbooks/roles/common/install-sync-software-metadata/tasks/main.yml b/playbookconfig/src/playbooks/roles/common/install-sync-software-metadata/tasks/main.yml new file mode 100644 index 000000000..00763a491 --- /dev/null +++ b/playbookconfig/src/playbooks/roles/common/install-sync-software-metadata/tasks/main.yml @@ -0,0 +1,141 @@ +--- +# +# Copyright (c) 2024 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# ROLE DESCRIPTION: +# This role is for install only. It synchronizes the system controller +# /opt/software/metadata directory to the subcloud. This is only done if the +# system controller and subcloud are at the same ostree commit. +# + +- name: Retrieve software version number + shell: source /etc/build.info; echo $SW_VERSION + register: sw_version + +- name: Set software metadata synchronization facts + set_fact: + software_sync_required: false + ostree_feed_repo_dir: /var/www/pages/feed/rel-{{ sw_version.stdout }}/ostree_repo + ostree_sysroot_repo_dir: /sysroot/ostree/repo + ostree_rev: starlingx + software_dir: /opt/software + +- block: + + - name: Gather system controller ostree commit + command: ostree --repo={{ ostree_feed_repo_dir }} rev-parse {{ ostree_rev }} + register: ostree_commit_system_controller + delegate_to: localhost + + - name: Gather subcloud ostree commit + command: ostree --repo={{ ostree_feed_repo_dir }} rev-parse {{ ostree_rev }} + register: ostree_commit_subcloud + + - name: Gather system controller updates list + shell: software list | grep {{ sw_version.stdout }} | grep -E '(available|deployed|committed)' + register: system_controller_software_list + failed_when: false + delegate_to: localhost + + - name: Gather subcloud software list + shell: software list | grep {{ sw_version.stdout }} | grep -E '(available|deployed|committed)' + register: subcloud_software_list + failed_when: false + + - name: Decide if software metadata synchronization is required + # Conditions: + # 1) the system controller and subcloud have different results from 'software list' + # or + # 2) The subcloud is not be at the same ostree commit level as the system controller. + # This can happen if the subcloud is installed from ISO (release or prestage). + set_fact: + software_sync_required: true + when: (system_controller_software_list.stdout != subcloud_software_list.stdout) + and (ostree_commit_system_controller.stdout == ostree_commit_subcloud.stdout) + + - debug: + msg: + - "Software sync required: {{ software_sync_required }}" + - "ostree revision from {{ ostree_feed_repo_dir }}:" + - "system controller: {{ ostree_commit_system_controller.stdout }}" + - "subcloud: {{ ostree_commit_subcloud.stdout }}" + - "Software list:" + - "system controller:" + - "{{ system_controller_software_list.stdout }}" + - "subcloud:" + - "{{ subcloud_software_list.stdout }}" + + - debug: + msg: + - "Skipping software metadata synchronization." + when: not software_sync_required + + - block: + - debug: + msg: "Synchronizing {{ software_dir }} metadata to subcloud..." + + - name: Ensure subcloud {{ software_dir }} exists (sysadmin) + file: + path: "{{ software_dir }}" + owner: sysadmin + group: root + state: directory + mode: 0755 + recurse: yes + become: true + + - name: Synchronize software dir from system controller to subcloud + synchronize: + mode: push + src: "{{ software_dir }}/" + dest: "{{ software_dir }}/" + rsync_opts: "--delete" + register: software_transfer + retries: 2 + delay: 5 + until: software_transfer.rc == 0 + + - name: Restore root ownership to subcloud {{ software_dir }} + file: + path: "{{ software_dir }}" + state: directory + owner: root + recurse: yes + become: true + + # TODO(kmacleod) This has to change. We'll have different versions for + # the upgrades in USM + # - name: Delete patch metadata files not belonging to the current software version + # ({{ sw_version.stdout }}) + # shell: > + # grep -L "{{ sw_version.stdout }}" {{ item }} + # | xargs -I {} sh -c 'echo "$1"; rm -f "$1"' sh {} + # loop: + # - "{{ software_dir }}/metadata/available/*" + # - "{{ software_dir }}/metadata/deployed/*" + # - "{{ software_dir }}/metadata/committed/*" + # register: deleted_metadata_files + # become: true + # + # - name: Print deleted software metadata files + # debug: + # msg: "{{ deleted_metadata_files.results | map(attribute='stdout_lines') + # | flatten | join('\n') }}" + # when: deleted_metadata_files.results | map(attribute='stdout_lines') + # | flatten | length > 0 + + # Restart the software controller and agent to pickup the changes + - name: Restart the software controller and agent + systemd: + name: "{{ item }}" + state: restarted + with_items: + - software-controller-daemon + - software-agent + become: true + + when: software_sync_required + + when: sync_software_metadata and sw_version.stdout is version('24.03', '>=') diff --git a/playbookconfig/src/playbooks/roles/prestage/get-prestage-versions/tasks/main.yml b/playbookconfig/src/playbooks/roles/prestage/get-prestage-versions/tasks/main.yml index 871afda11..62ec62a50 100644 --- a/playbookconfig/src/playbooks/roles/prestage/get-prestage-versions/tasks/main.yml +++ b/playbookconfig/src/playbooks/roles/prestage/get-prestage-versions/tasks/main.yml @@ -29,8 +29,8 @@ - name: Extract and sort version numbers set_fact: - prestege_versions: "{{ parent_dirs | sort | join(',') }}" + prestage_versions: "{{ parent_dirs | sort | join(',') }}" - name: Print prestage versions debug: - msg: "prestage_versions: {{ prestege_versions }}" + msg: "prestage_versions: {{ prestage_versions }}" diff --git a/playbookconfig/src/playbooks/roles/prestage/prepare-env/tasks/main.yml b/playbookconfig/src/playbooks/roles/prestage/prepare-env/tasks/main.yml index 51fa497b6..d0a065285 100644 --- a/playbookconfig/src/playbooks/roles/prestage/prepare-env/tasks/main.yml +++ b/playbookconfig/src/playbooks/roles/prestage/prepare-env/tasks/main.yml @@ -9,36 +9,42 @@ # for packages/images prestaging. # -- name: Fail if software version for images prestaging is not specified +- name: Fail if software version for prestaging is not specified fail: msg: "Please specify software version for prestaging." when: software_version is not defined +- name: Fail if software major release for prestaging is not specified + fail: + msg: "Please specify software major version for prestaging." + when: software_major_release is not defined + - name: Get minimum supported release version import_role: name: common/minimum-upgradable-release -- name: Fail if software version is not supported for prestaging +- name: Fail if software major release is not supported for prestaging fail: msg: >- - The specified software version {{ software_version }} is not supported. + The specified software major release {{ software_major_release }} is not supported. Minimum supported release versions for prestage: {{ minimum_supported_release_version }} - when: software_version is version(minimum_supported_release_version, "<") + when: software_major_release is version(minimum_supported_release_version, "<") - name: Set prestage software version and cleanup directores fact set_fact: - prestage_software_version: "{{ software_version }}" - cleanup_dirs: "{{ cleanup_dirs|default([]) + [software_version] }}" + cleanup_dirs: "{{ cleanup_dirs|default([]) + [software_major_release] }}" - name: Set prestage_os_type set_fact: - prestage_os_type: "{{ 'debian' if prestage_software_version is version('22.12', '>=') else 'centos' }}" + prestage_os_type: "{{ 'debian' if software_major_release is version('22.12', '>=') else 'centos' }}" - name: Set prestage dir and subdirectory facts set_fact: prestage_source: remote - prestage_dir: "{{ platform_backup_dir }}/{{ prestage_software_version }}" - patches_prestage_dir: "{{ platform_backup_dir }}/{{ prestage_software_version }}/patches" + prestage_dir: "{{ platform_backup_dir }}/{{ software_major_release }}" + host_software_metadata_dir: "/opt/software/metadata" + prestage_sync_software_metadata_required: false + patches_prestage_dir: "{{ platform_backup_dir }}/{{ software_major_release }}/patches" host_patch_metadata_dir: "/opt/patching/metadata" - name: Retrieve current software version of the host @@ -53,7 +59,7 @@ - name: Change prestage_source to local set_fact: prestage_source: local - when: prestage_software_version == host_software_version + when: software_major_release == host_software_version - name: Prepare prestaging packages include_tasks: prepare-sw-packages-prestage-env.yml diff --git a/playbookconfig/src/playbooks/roles/prestage/prepare-env/tasks/prepare-sw-packages-prestage-env.yml b/playbookconfig/src/playbooks/roles/prestage/prepare-env/tasks/prepare-sw-packages-prestage-env.yml index 7558871b0..1319823c0 100644 --- a/playbookconfig/src/playbooks/roles/prestage/prepare-env/tasks/prepare-sw-packages-prestage-env.yml +++ b/playbookconfig/src/playbooks/roles/prestage/prepare-env/tasks/prepare-sw-packages-prestage-env.yml @@ -1,6 +1,6 @@ --- # -# Copyright (c) 2023 Wind River Systems, Inc. +# Copyright (c) 2023-2024 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -9,23 +9,27 @@ # packages/ostree_repo prestaging. # +# When is prestage_source local vs. remote? +# - local: if we are prestaging the same software version as currently running on system controller +# - remote: if we are prestaging a different software version than currently running on system controller + - block: - name: Set feed and patch directories to /www/pages/... (Centos and local source) set_fact: - release_feed: "/www/pages/feed/rel-{{ prestage_software_version }}" - host_patch_repo: "/www/pages/updates/rel-{{ prestage_software_version }}" + release_feed: "/www/pages/feed/rel-{{ software_major_release }}" + host_patch_repo: "/www/pages/updates/rel-{{ software_major_release }}" when: prestage_source == 'local' - name: Set feed and patch directories to /var/www/pages/... (Centos and remote source) set_fact: - release_feed: "/var/www/pages/feed/rel-{{ prestage_software_version }}" - host_patch_repo: "/var/www/pages/updates/rel-{{ prestage_software_version }}" + release_feed: "/var/www/pages/feed/rel-{{ software_major_release }}" + host_patch_repo: "/var/www/pages/updates/rel-{{ software_major_release }}" when: prestage_source == 'remote' when: prestage_os_type == 'centos' - name: Set the feed directory to /var/www/pages/feed (Debian) set_fact: - release_feed: "/var/www/pages/feed/rel-{{ prestage_software_version }}" + release_feed: "/var/www/pages/feed/rel-{{ software_major_release }}" when: prestage_os_type == 'debian' - name: Prepare prestage packages for Centos @@ -101,6 +105,30 @@ ostree_repo_release_feed: "{{ release_feed }}/ostree_repo" - block: + - name: Check if {{ host_software_metadata_dir }} exists on {{ inventory_hostname }} + stat: + path: "{{ host_software_metadata_dir }}" + register: host_software_dir_result + + - name: Set flag to indicate if the host software dir exist + set_fact: + host_software_exist: "{{ host_software_dir_result.stat.exists }}" + when: host_software_dir_result is not skipped + - name: Check if {{ host_software_metadata_dir }} exists on {{ inventory_hostname }} + stat: + path: "{{ host_software_metadata_dir }}" + register: host_software_dir_result + + - name: Check if any minor upgrades have been applied + shell: software list --release {{ software_major_release }} | tail +4 | grep -c -E '^\| ' + register: system_controller_software_num_releases + delegate_to: localhost + + - name: Set flag to indicate if minor upgrades have been applied + set_fact: + prestage_sync_software_metadata_required: >- + "{{ true if system_controller_software_num_releases.stdout != '1' else false }}" + - name: Check if {{ host_patch_metadata_dir }} exists on {{ inventory_hostname }} stat: path: "{{ host_patch_metadata_dir }}" @@ -124,6 +152,17 @@ msg: "Directory {{ ostree_repo_release_feed }} does not exist on the system controller." when: not ostree_repo_release_feed_remote.stat.exists + - name: Check if {{ host_software_metadata_dir }} exists on the system controller + stat: + path: "{{ host_software_metadata_dir }}" + register: host_software_dir_result + delegate_to: localhost + + - name: Set flag to indicate if the host software dir exist + set_fact: + host_software_dir_exist: "{{ host_software_dir_result.stat.exists }}" + when: host_software_dir_result is not skipped + - name: Check if {{ host_patch_metadata_dir }} exists on the system controller stat: path: "{{ host_patch_metadata_dir }}" @@ -140,6 +179,8 @@ - debug: msg: | Prestaging type: {{ prestage_type }} + Prestaging install: {{ prestage_install }} Prestaging source: {{ prestage_source }} Prestaging os type: {{ prestage_os_type }} Release feed directory (on target): {{ release_feed|default('N/A') }} + prestage_sync_software_metadata_required: {{ prestage_sync_software_metadata_required }} diff --git a/playbookconfig/src/playbooks/roles/prestage/prestage-patches/tasks/prestage-patch-metadata.yml b/playbookconfig/src/playbooks/roles/prestage/prestage-patches/tasks/prestage-patch-metadata.yml index 30c32891f..8eb2a1073 100644 --- a/playbookconfig/src/playbooks/roles/prestage/prestage-patches/tasks/prestage-patch-metadata.yml +++ b/playbookconfig/src/playbooks/roles/prestage/prestage-patches/tasks/prestage-patch-metadata.yml @@ -49,7 +49,7 @@ - name: Delete those patch metadata files not belonging to the prestage software version shell: > - grep -L "{{ prestage_software_version }}" + grep -L "{{ software_major_release }}" {{ item }} 2>/dev/null | xargs rm -f loop: - "{{ patches_prestage_dir }}/metadata/applied/*" diff --git a/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/ostree-metadata-sync.sh b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/ostree-metadata-sync.sh new file mode 100755 index 000000000..47dc6ca3b --- /dev/null +++ b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/ostree-metadata-sync.sh @@ -0,0 +1,694 @@ +#!/bin/bash +# +# Copyright (c) 2024 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# vim: filetype=sh shiftwidth=4 softtabstop=4 expandtab + +set -o nounset; # Do not allow use of undefined vars. Use ${VAR:-} to use an undefined VAR. Same as 'set -u' +set -o pipefail; # Catch the error in case a piped command fails +# set -o xtrace; # Turn on traces, useful while debugging (short form: on: 'set -x' off: 'set +x') + +################################################################################ +# +# Testing: +# This script has unit tests. Run the unit tests manually via: ./test/run-bats.sh +# +################################################################################ +# +# Structure: +# This script has two top-level modes of operation, based on the subcommands: +# - get-commits (implementation: find_all_ostree_commits_for_release) +# - sync-subcloud (implementation: sync_subcloud_metadata) +# +################################################################################ + +################################################################################ +# Helpers +# + +# shellcheck disable=SC2155 +readonly SCRIPTNAME=$(basename "$0") +# shellcheck disable=SC2155,SC2034 +#readonly SCRIPTDIR=$(readlink -m "$(dirname "$0")") + +SW_VERSION=${SW_VERSION:-} + +DEBUG=${DEBUG:-} +DRY_RUN=${DRY_RUN:-} + +help() { +cat< [ get-commits | sync-subcloud ] + +The script behaves differently depending on the 'get-commits' or 'sync-subcloud' subcommand: + + get-commits : For the given major software version, get a list of all metadata file + ostree commit hash + Returns a list of: : + If the ostree_commit_hash is not known then this field is set to '-' + + sync-subcloud : Synchronize /opt/software/metadata directory on the subcloud. + This subcommand *must be run as root*, and is executed on the subcloud + via ansible, after setting up proper contents of the \$METADATA_SYNC_DIR + See documentation in sync_metadata_on_subcloud() for algorithm details. + +OPTIONS: + + -o|--output : Save script output to file. + -v|--sw-version : Software version being synchronized. + + -D|--debug: Show extra debug information. + --dry-run: Dry run. Makes no changes. + -h|--help: print this help + +EXAMPLES: + + $SCRIPTNAME --sw-version 24.09 get-commits + sudo $SCRIPTNAME --sw-version 24.09 sync-subcloud +EOF +exit 1 +} + +# Logging: these all log to stderr +die() { >&2 colorecho red "FATAL: $*"; exit 1; } +die_with_rc() { local rc=$1; shift; >&2 colorecho red "FATAL: $*, rc=$rc"; exit "$rc"; } +check_rc_die() { local rc=$1; shift; [ "$rc" != "0" ] && die_with_rc "$rc" "$@"; return 0; } +check_rc_err() { local rc=$1; shift; [ "$rc" != "0" ] && log_error "$*, rc=$rc"; return 0; } +log_error() { >&2 colorecho red "ERROR: $*"; } +log_warn() { >&2 colorecho orange "WARN: $*"; } +log_info() { >&2 echo "$*"; } +log_info_l() { + local line spacer='' + for line in "$@"; do + [ -n "${line}" ] && >&2 echo "${spacer}${line}" + spacer=' ' + done +} +log_debug() { if [ -n "$DEBUG" ]; then >&2 echo "DEBUG: $*"; fi; } +log_debug_l() { + [ -z "$DEBUG" ] && return + local line spacer='' + for line in "$@"; do + [ -n "${line}" ] && >&2 echo "${spacer}${line}" + spacer=' ' + done +} +log_progress() { >&2 colorecho green "$*"; } +get_logdate() { date '+%Y-%m-%d %H:%M:%S'; } # eg: log_info "$(get_logdate) My log message" +# Optionals to log output to file (see http://mywiki.wooledge.org/BashFAQ/106) +_init_log() { LOG_FILE="${LOG_FILE:-$(pwd)/${SCRIPTNAME%.*}.log}"; log_progress "$(get_logdate) Logging output to $LOG_FILE"; } +# output to file only: +redirect_output_to_file() { _init_log; exec &> "$LOG_FILE"; } +# output to console and file: +tee_output_to_file_single_process() { _init_log; exec &> >(exec tee "$LOG_FILE"); } # see https://superuser.com/a/1534702 +tee_output_to_file() { _init_log; exec &> >(tee "$LOG_FILE"); } + +colorecho() { # usage: colorecho or colorecho -n + local echo_arg= + if [ "$1" = "-n" ]; then + echo_arg="-n"; shift + fi + local colour="$1"; shift + case "${colour}" in + red) echo $echo_arg -e "$(tput setaf 1)$*$(tput sgr0)"; ;; + green) echo $echo_arg -e "$(tput setaf 2)$*$(tput sgr0)"; ;; + green-bold) echo $echo_arg -e "$(tput setaf 2; tput bold)$*$(tput sgr0)"; ;; + yellow) echo $echo_arg -e "$(tput setaf 3; tput bold)$*$(tput sgr0)"; ;; + orange) echo $echo_arg -e "$(tput setaf 3)$*$(tput sgr0)"; ;; + blue) echo $echo_arg -e "$(tput setaf 4)$*$(tput sgr0)"; ;; + purple) echo $echo_arg -e "$(tput setaf 5)$*$(tput sgr0)"; ;; + cyan) echo $echo_arg -e "$(tput setaf 6)$*$(tput sgr0)"; ;; + bold) echo $echo_arg -e "$(tput bold)$*$(tput sgr0)"; ;; + normal|*) echo $echo_arg -e "$*"; ;; + esac +} + + +################################################################################ +# +# Utilities +# +################################################################################ + +initialize_env() { + if [ -f /etc/platform/openrc ]; then + # shellcheck disable=SC1091 + source /etc/platform/openrc + else + # unit testing + log_warn "not found: /etc/platform/openrc" + fi + + export METADATA_DIR=${METADATA_DIR:-/opt/software/metadata} + export METADATA_SYNC_DIR=${METADATA_SYNC_DIR:-/opt/software/tmp/metadata-sync} + export METADATA_SYNC_METADATA_DIR=${METADATA_SYNC_DIR}/metadata + + # shellcheck disable=SC1091 + if [ -z "${SW_VERSION}" ]; then + source /etc/build.info + fi + export SW_VERSION + + local version_array + IFS='.' read -ra version_array <<< "${SW_VERSION}" + MAJOR_SW_VERSION=$(get_major_release_version "${SW_VERSION}") + export MAJOR_SW_VERSION + + export OSTREE_REPO="/var/www/pages/feed/rel-${MAJOR_SW_VERSION}/ostree_repo" + export OSTREE_REMOTE=starlingx + export OSTREE_BRANCH=starlingx + export OSTREE_LOCAL_REF="${OSTREE_REMOTE}" + export OSTREE_REMOTE_REF="${OSTREE_REMOTE}:${OSTREE_BRANCH}" + export OSTREE_HTTP_PORT=8080 + export OSTREE_HTTPS_PORT=8443 + + log_debug_l "SW_VERSION: ${SW_VERSION}"\ + "MAJOR_SW_VERSION: ${MAJOR_SW_VERSION}"\ + "OSTREE_REPO: ${OSTREE_REPO}"\ + "OSTREE_LOCAL_REF: ${OSTREE_LOCAL_REF}"\ + "OSTREE_REMOTE_REF: ${OSTREE_REMOTE_REF}"\ + "METADATA_DIR: ${METADATA_DIR}"\ + "METADATA_SYNC_DIR: ${METADATA_SYNC_DIR}" +} + + +trim() { + # Trim whitespace from string + # see https://stackoverflow.com/a/3352015 + local var="$*" + # remove leading whitespace characters + var="${var#"${var%%[![:space:]]*}"}" + # remove trailing whitespace characters + var="${var%"${var##*[![:space:]]}"}" + printf '%s' "$var" +} + +get_major_release_version() { + # The given sw_version may be in form YY.MM.nn or just YY.MM + # Get the major release (YY.MM) by splitting on '.' into an + # array then constructing MAJOR_SW_VERSION from it + local sw_version=$1 + local version_array + IFS='.' read -ra version_array <<< "${sw_version}" + echo "${version_array[0]}.${version_array[1]}" +} + +find_metadata_files_for_release_sorted() { + # Find all metadata files for given software release (major, e.g YY.MM or minor YY.MM.nn) + # The files are sorted in order of minor release version, ascending + # For minor release we should only find one metadata file + # + local sw_version=${1:-$SW_VERSION} + local metadata_dir=${2:-$METADATA_DIR} + + # 1) Get all the sw_version metadata files matching the major/minor software version we're given + # Storing in a associative array + local meta_file + local -A metadata_files_map # key: sw_version, value: metadata file + local found_sw_version + while IFS= read -r meta_file; do + found_sw_version=$(get_simple_xml_attrib_from_metadata "${meta_file}" "sw_version") + metadata_files_map[${found_sw_version}]=${meta_file} + done < <(grep --recursive --files-with-matches --fixed-strings "${sw_version}" "${metadata_dir}") + + if [ ${#metadata_files_map[@]} -eq 0 ]; then + return + fi + + # 2) Sort by sw_version tag (regardless of path) + local sorted_versions=() + while IFS= read -rd '' found_sw_version; do + sorted_versions+=("${found_sw_version}") + done < <(printf '%s\0' "${!metadata_files_map[@]}" | sort --zero-terminated --version-sort) + + # 3) Return the list of files in sorted order + local sorted_version + for sorted_version in "${sorted_versions[@]}"; do + echo "${metadata_files_map[${sorted_version}]}" + done +} + +find_metadata_file_for_attrib_val() { + local attrib_name=$1 + local attrib_val=$2 + local metadata_dir=${3:-$METADATA_DIR} + local meta_file + local -a metadata_files=() + while IFS= read -r meta_file; do + metadata_files+=( "${meta_file}" ) + done < <(grep --recursive --files-with-matches --fixed-strings "<${attrib_name}>${attrib_val}" "${metadata_dir}") + if [ ${#metadata_files[*]} -eq 1 ]; then + log_debug "find_metadata_file_for_attrib_val: attrib: ${attrib_name}, value: ${attrib_val}, file: ${metadata_files[0]}" + echo "${metadata_files[0]}" + elif [ ${#metadata_files[*]} -gt 1 ]; then + die "find_metadata_file_for_attrib_val unexpected: found multiple metadata files for ${attrib_name} ${attrib_val} in ${metadata_dir}: ${metadata_files[*]}" + fi +} + +get_simple_xml_attrib_from_metadata() { + # Retrieve the value of given attribute. + # WARNING: This function performs very basic parsing: + # It only works if the opening and closing + # are on the same line. + local meta_file=$1 + local attrib=$2 + local val + val=$(sed -n 's|<'"${attrib}"'>\(.*\)|\1|p' "${meta_file}") + val=$(trim "${val}") + log_debug "metadata GET ${attrib}: ${val}" + echo "${val}" +} + +get_commit_hashes_from_metadata() { + # Using a nameref to update the passed-in array, + # see https://mywiki.wooledge.org/BashProgramming?highlight=%28nameref%29#Functions + local -n from_metadata_commit_hashes=$1 + local meta_file=$2 + local commit + while IFS= read -r commit; do + commit=$(trim "${commit}") + from_metadata_commit_hashes+=( "${commit}" ) + done < <(sed --quiet 's|\(.*\)|\1|p' "${meta_file}") +} + +get_usm_state_from_path() { + local path=$1 + local state + case "${path}" in + */available/*) + state=available + ;; + */committed/*) + state=committed + ;; + */deployed/*) + state=deployed + ;; + */deploying/*) + state=deploying + ;; + */removing/*) + state=removing + ;; + */unavailable/*) + state=unavailable + ;; + *) + log_error "get_usm_state_from_path: parse failure: path='${path}'" + state=unavailable + ;; + esac + log_debug "get_usm_state_from_path: path=${path}, state: ${state}" + echo "${state}" +} + +ostree_commit_exists() { + # Does given commit exist in ostree? i.e. has it been pulled into our repo yet? + # Note: this only works for locally defined ostree repositories. + # i.e. it can't get status from a remote server + local commit_hash=$1 + local ref=${2:-${OSTREE_LOCAL_REF}} + ostree --repo="${OSTREE_REPO}" log "${ref}" | grep '^commit ' | grep --quiet "${commit_hash}" +} + +translate_central_metadata_path() { + # translate the /opt/software/metadata/... path to /opt/software/tmp/metadata-sync/metadata... + local metadata_file=$1 + echo "${metadata_file/#"${METADATA_DIR}"/"${METADATA_SYNC_METADATA_DIR}"}" +} + +get_metadata_files_unique_to_central() { + # TODO ISSUE: + # This gets flagged for removal which it shouldn't - it's just a stage change: + # + # [sysadmin@controller-0 ~(keystone_admin)]$ diff -s /opt/software/tmp/metadata-sync/ostree-metadata-commits.* + # 1d0 + # < /opt/software/metadata/deployed/starlingx-24.09.1-metadata.xml:db313865837f9512b024a2356bd76106140ebcea783f8183e5fcc8d5cd28783b + # 2a2 + # > /opt/software/metadata/available/starlingx-24.09.1-metadata.xml:db313865837f9512b024a2356bd76106140ebcea783f8183e5fcc8d5cd28783b + + diff "${METADATA_SYNC_DIR}"/ostree-metadata-commits.{central,subcloud} | awk '/^/ {print $2;}' | awk -F ':' '{print $1;}' +} + +pull_ostree_commit_to_subcloud() { + # Pulls given commit into subcloud feed repo + # + local commit_hash=$1 + if ostree_commit_exists "${commit_hash}"; then + log_info "ostree commit ${commit_hash}: already exists in ${OSTREE_LOCAL_REF}" + else + log_info "Pulling ostree commit from system controller: ${commit_hash}" + run_cmd ostree --repo="${OSTREE_REPO}" pull --mirror "${OSTREE_REMOTE_REF}" "${OSTREE_BRANCH}@${commit_hash}" + check_rc_die $? "ostree pull failed" + fi +} + +configure_ostree_repo_for_central_pull() { + # Ensures the $OSTREE_REPO is configured to pull from the system controller + [ -n "${DRY_RUN}" ] && return + + # Get system controller management IP (run from system controller): + local system_controller_ip + system_controller_ip=$(system addrpool-list | awk '/system-controller-subnet/ { print $14; }') + + local is_https_enabled + is_https_enabled=$(system show | awk '/https_enabled/ { print $4; }') + + log_info_l "Configuring ostree repo: "\ + "system_controller_ip: ${system_controller_ip}"\ + "is_https_enabled: ${is_https_enabled}"\ + "OSTREE_REPO: ${OSTREE_REPO}" + + if [ "${is_https_enabled}" = True ]; then + sed -i.bak 's|^url=.*|url=https://'"${system_controller_ip}:${OSTREE_HTTPS_PORT}/iso/${MAJOR_SW_VERSION}/ostree_repo"'|' "${OSTREE_REPO}/config" + if ! grep --quiet 'tls-permissive=true' "${OSTREE_REPO}/config"; then + echo "tls-permissive=true" >> "${OSTREE_REPO}/config" + fi + else + sed -i.bak 's|^url=.*|url=http://'"${system_controller_ip}:${OSTREE_HTTP_PORT}/iso/${MAJOR_SW_VERSION}/ostree_repo"'|' "${OSTREE_REPO}/config" + fi +} + +run_cmd() { + if [ -n "${DRY_RUN}" ]; then + log_info "DRY_RUN: $*" + else + log_info "Running: $*" + "$@" + fi +} + + +################################################################################ +# +# Top-level command implementation +# +################################################################################ + +find_all_ostree_commits_for_release() { + # + # Find all ostree commits for the given sw version. + # + # Returns a list of: : + # for the local metadata tree. + # + # If the metadata does not specify a commit-id then we use '-' + # The list is sorted by version, from lowest to highest. + # This ensures that versions can be processed in the correct numerical order. + # + local sw_version=${1:-$SW_VERSION} + local metadata_dir=${2:-$METADATA_DIR} + local number_of_commits metadata_file + for metadata_file in $(find_metadata_files_for_release_sorted "${sw_version}" "${metadata_dir}"); do + if [ ! -f "${metadata_file}" ]; then + return + fi + number_of_commits=$(get_simple_xml_attrib_from_metadata "${metadata_file}" "number_of_commits") + + # TODO Testing with multiple commits in an update is incomplete + # remove this when fully tested: + if [ -n "${number_of_commits}" ] && [ "${number_of_commits}" -gt 1 ]; then + log_warn "Update has ${number_of_commits} commits: not tested yet" + fi + + local commit_hashes=() + get_commit_hashes_from_metadata commit_hashes "${metadata_file}" + if [ "${#commit_hashes[@]}" -eq 0 ]; then + echo "${metadata_file}:-" + else + if [ "${number_of_commits}" -ne "${#commit_hashes[@]}" ]; then + # Unexpected, and we should fail here + die "Update has number_of_commits=${number_of_commits} but only found ${#commit_hashes[@]} commits" + fi + + # We only need to supply the first commit here. + # See how the sync_subcloud_metadata algorithm works - it only uses the first commit + # TODO: do we actually need to supply the commit at all? + echo "${metadata_file}:${commit_hashes[0]}" + fi + done +} + +sync_metadata_on_subcloud() { + # + # This function peforms metadata / ostree commit synchronizaton on the subcloud + # + # The algorithm for syncing the /opt/software/metadata//-metadata.xml + # is as follows: + # + # For each RELEASE being synchronized from systemController: + # + # COPY metadata.xml from systemController + # - this will include the 'ostree-commit-id' and 'committed' ATTRIBUTES from systemController + # * this has already been done by ansible + # + # IF RELEASE does NOT EXIST on subcloud + # IF 'ostree-commit-id' == NULL + # Create it with STATE = unavailable + # ELSE + # Create it with STATE = available + # ELSE // RELEASE exists on subcloud + # IF subcloud STATE == deployed + # Leave it as deployed + # ELSE IF subcloud STATE == available or unavailable + # IF ‘ostree-commit-id’ == NULL + # Set STATE = unavailable + # ELSE + # Set STATE = available + # + # For each RELEASE on SUBCLOUD but NOT synchronized from systemController + # REMOVE RELEASE FROM SUBCLOUD + # + local metadata_file=$1 + local central_metadata_file=$2 + + # Using a namedref: use different name to avoid name collision + # See https://mywiki.wooledge.org/BashProgramming?highlight=%28nameref%29#Functions + local -n sync_subcloud_commit_hashes=$3 + + # We already have the metadata file from the system controller + if [ -z "${central_metadata_file}" ]; then + # unexpected + die "no metadata file found for ostree commit(s): ${sync_subcloud_commit_hashes[*]}" + fi + + # Get current subcloud state from metadata; it may or may not exist + local id sw_release central_usm_state subcloud_metadata_file + id=$(get_simple_xml_attrib_from_metadata "${central_metadata_file}" "id") + sw_release=$(get_simple_xml_attrib_from_metadata "${central_metadata_file}" "sw_release") + central_usm_state=$(get_usm_state_from_path "$central_metadata_file") + subcloud_metadata_file=$(find_metadata_file_for_attrib_val "id" "${id}" "${METADATA_DIR}") + + local log_hdr="sync_metadata_on_subcloud: id: ${id}" + log_info_l "${log_hdr}" "sw_release: ${sw_release}"\ + "commit_hashes: ${sync_subcloud_commit_hashes[*]}"\ + "central_metadata_file: ${central_metadata_file}"\ + "central_usm_state: ${central_usm_state}"\ + "subcloud_metadata_file: ${subcloud_metadata_file}" + + local new_state + # It is sufficient to check against only one commit hash here - they are all part of the same metadata file + local commit_hash=${sync_subcloud_commit_hashes[0]} + if [ -z "${subcloud_metadata_file}" ]; then + # Not found: RELEASE does NOT EXIST on subcloud + if ostree_commit_exists "${commit_hash}"; then + # Create it with STATE = available + log_debug_l "sync_metadata_on_subcloud: commit exists in local ${OSTREE_LOCAL_REF}"\ + "ref: ${commit_hash}" + new_state="available" + else + # Create it with STATE = unavailable + new_state="unavailable" + fi + log_info "${log_hdr} does not exist on subcloud, setting to ${new_state}" + run_cmd cp "${central_metadata_file}" "${METADATA_DIR}/${new_state}" + else + # RELEASE exists on subcloud + local subcloud_state + subcloud_state=$(get_usm_state_from_path "${subcloud_metadata_file}") + case "${subcloud_state}" in + 'deployed') + # Leave it as deployed + log_info "${log_hdr} is in sync (subcloud state: deployed)" + ;; + 'available'|'unavailable') + # Not found: RELEASE does NOT EXIST on subcloud + if ostree_commit_exists "${commit_hash}"; then + # Set STATE = available + log_debug_l "sync_metadata_on_subcloud: commit exists in local ${OSTREE_LOCAL_REF}"\ + "ref: ${commit_hash}" + new_state=available + else + # Set STATE = unavailable + new_state=unavailable + fi + log_info "${log_hdr} exists on subcloud, setting subcloud state: ${new_state}" + run_cmd rm "${subcloud_metadata_file}" + run_cmd cp "${central_metadata_file}" "${METADATA_DIR}/${new_state}" + ;; + 'committed') + log_info "${log_hdr} subcloud state is ${subcloud_state} - ignoring" + ;; + 'deploying'|'removing') + log_info "${log_hdr} subcloud state is ${subcloud_state} - transitional, ignoring" + ;; + *) + log_error "${log_hdr} subcloud state is unexpected: ${subcloud_state} - ignoring" + ;; + esac + fi +} + +# Context: INVOKED ON SUBCLOUD +sync_subcloud_metadata() { + # + # Top-level function to synchronize the subcloud software metadata. + # + # When this is invoked, we have the following in place (via ansible): + # + # - "${METADATA_SYNC_DIR}"/ostree-metadata-commits.{central,subcloud} + # - these files summarizing the metadata files / ostree commits matching our given release + # - "${METADATA_SYNC_DIR}/metadata + # - is a direct copy of the system controller /opt/software/medatada directory + # - we use this to calculate the new subcloud state of the release + # and to pull the missing ostree commits to the subcloud + # + # Synchronization is done on a per-major-release basis. + # For given major release: + # 1) Get a list of all update metadata files needing to be synchronized + # (this is done by comparing (using diff) the central and subcloud file in + # "${METADATA_SYNC_DIR}"/ostree-metadata-commits.{central,subcloud}). + # 2) Ensure any ostree commit(s) for the update are pulled from central + # controller if necessary. + # 3) Synchronize the update metadata file into the proper state-based location + # on the subcloud + # + local metadata_file commit_hash central_metadata_file + + configure_ostree_repo_for_central_pull + + local commit_hashes=() + local commit_hash + # 1) Get list of metadata files requiring sync + for metadata_file in $(get_metadata_files_unique_to_central); do + log_info "sync_subcloud_metadata: processing ${metadata_file} from central (sync)" + central_metadata_file=$(translate_central_metadata_path "${metadata_file}") + + get_commit_hashes_from_metadata commit_hashes "${central_metadata_file}" + log_debug_l "sync_subcloud_metadata from central: "\ + "metadata_file: ${metadata_file}"\ + "central_metadata_file: ${central_metadata_file}"\ + "commit_hashes: ${commit_hashes[*]}" + + if [ "${#commit_hashes[@]}" -gt 0 ]; then + for commit_hash in "${commit_hashes[@]}"; do + # 2) Pull from central controller if necessary + + # TODO(kmacleod): check if previous_commit exists from metadata, fail + + pull_ostree_commit_to_subcloud "${commit_hash}" + done + # 3) Synchronize the metadata file + sync_metadata_on_subcloud "${metadata_file}" "${central_metadata_file}" commit_hashes + fi + done + for metadata_file in $(get_metadata_files_unique_to_subcloud); do + log_info "sync_subcloud_metadata: processing ${metadata_file} from subcloud (check remove)" + commit_hashes=() + get_commit_hashes_from_metadata commit_hashes "${central_metadata_file}" + log_debug_l "sync_subcloud_metadata from subcloud (check remove): "\ + "metadata_file: ${metadata_file}"\ + "commit_hashes: ${commit_hashes[*]}" + local removed= + if [ "${#commit_hashes[@]}" -gt 0 ]; then + for commit_hash in "${commit_hashes[@]}"; do + if ! ostree_commit_exists "${commit_hash}"; then + log_info "sync_subcloud_metadata from subcloud: commit '${commit_hash}' does not exist, removing '${metadata_file}'" + removed=1 + fi + done + if [ -n "${removed}" ]; then + rm "${metadata_file}" + fi + fi + if [ -n "${removed}" ]; then + log_info_l "sync_subcloud_metadata from subcloud, removed file for non-existing commit(s): "\ + "metadata_file: ${metadata_file}"\ + "commit_hashes: ${commit_hashes[*]}" + else + log_info_l "sync_subcloud_metadata from subcloud, commit is in use, not removing: "\ + "metadata_file: ${metadata_file}"\ + "commit_hashes: ${commit_hashes[*]}" + fi + done +} + +################################################################################ +# Main +# +main() { + local arg_outputfile= + local -a cmd + while [ $# -gt 0 ] ; do + case "${1:-""}" in + -h|--help) + help + ;; + -D|--debug) + DEBUG=1 + ;; + --dry-run) + DRY_RUN=1 + ;; + -o|--output) + shift + arg_outputfile=$1 + ;; + -v|--sw-version) + shift + SW_VERSION=$1 + export SW_VERSION + ;; + get-commits) + shift + cmd=('find_all_ostree_commits_for_release') + break + ;; + sync-subcloud) + if [ "$(id -u)" != 0 ]; then + die "you must be root to run sync-commits" + fi + shift + cmd=('sync_subcloud_metadata') + break + ;; + *) + die "Invalid command '$1' [use -h/--help for help]" + ;; + esac + shift + done + + initialize_env + + # execute our command + if [ -z "${arg_outputfile}" ]; then + "${cmd[@]}" + else + [ -f "${arg_outputfile}" ] && rm -f "${arg_outputfile}" + "${cmd[@]}" | tee "${arg_outputfile}" + fi +} + +if [[ "${BASH_SOURCE[0]}" = "$0" ]]; then + main "$@" +fi diff --git a/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/Dockerfile b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/Dockerfile new file mode 100644 index 000000000..fb4e1c54a --- /dev/null +++ b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/Dockerfile @@ -0,0 +1,9 @@ +FROM bats/bats + +RUN \ + apk --no-cache --update add \ + coreutils \ + diffutils \ + grep \ + gawk \ + sed diff --git a/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/README.md b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/README.md new file mode 100644 index 000000000..67fe54751 --- /dev/null +++ b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/README.md @@ -0,0 +1,40 @@ +This is a unit test suite for the ostree-metadata-sync.sh bash script. + +Bats is the Bash Automated Testing System. See https://bats-core.readthedocs.io/ + +Usage: + +The tests are executed via the bats docker container. Use the run-bats.sh wrapper script to run the tests via bats inside the docker +container: + + cd $MY_REPO/stx/ansible-playbooks/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test + + # Run all tests + ./run-bats.sh + + # Run tests in interactive mode: + ./run-bats.sh --interactive + +See ./run-bats.sh --help for information. + + +TODO (as suggested by Yuxing): + +> I would suggest to try to run the bat test with tox/zuul rather than in another container: +> Something like: + +> [testenv:bats] +> basepython = python3 +> allowlist_externals = +> bats +> git +> commands = bats <> + +> [tox] +> envlist = linters,pep8,bats + +Note: this would require ensuring that bats in installed in the tox/zuul environment, which +is probably not trivial, and is likely the bulk of the effort required. + +Once this is dont, then other bash scripts could be tested using bats, which would be a valuable +addition to the test suite. \ No newline at end of file diff --git a/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test1/metadata/deployed/starlingx-24.03.0-metadata.xml b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test1/metadata/deployed/starlingx-24.03.0-metadata.xml new file mode 100644 index 000000000..eccd5327d --- /dev/null +++ b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test1/metadata/deployed/starlingx-24.03.0-metadata.xml @@ -0,0 +1,19 @@ + + + starlingx-24.03.0 + 24.03.0 + starlingx + STX 24.03 GA release + STX 24.03 major GA release + + + REL + Y + Y + + + + + + + diff --git a/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test1/tmp/metadata-sync/metadata/deployed/starlingx-24.03.0-metadata.xml b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test1/tmp/metadata-sync/metadata/deployed/starlingx-24.03.0-metadata.xml new file mode 100644 index 000000000..eccd5327d --- /dev/null +++ b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test1/tmp/metadata-sync/metadata/deployed/starlingx-24.03.0-metadata.xml @@ -0,0 +1,19 @@ + + + starlingx-24.03.0 + 24.03.0 + starlingx + STX 24.03 GA release + STX 24.03 major GA release + + + REL + Y + Y + + + + + + + diff --git a/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test1/tmp/metadata-sync/metadata/deployed/starlingx-24.03.1-metadata.xml b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test1/tmp/metadata-sync/metadata/deployed/starlingx-24.03.1-metadata.xml new file mode 100644 index 000000000..7fa7b7b7d --- /dev/null +++ b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test1/tmp/metadata-sync/metadata/deployed/starlingx-24.03.1-metadata.xml @@ -0,0 +1,27 @@ + + starlingx-24.03.1 + 24.03.1 + starlingx + Sample inservice test patch + This patch DOESN'T require reboot. + This patch should include 1 packages: + - logmgmt + Sample instructions + Sample warning + DEV + N + N + + + starlingx-24.03.0 + + pre-install.sh + post-install.sh + deploy-precheck.sh + + logmgmt_1.0-1.stx.9_all.deb + + 1 + 895ea56dd33882a91a872871f6989c7978ff1413404e2574693380186dbb09a7 + db313865837f9512b024a2356bd76106140ebcea783f8183e5fcc8d5cd28783b + \ No newline at end of file diff --git a/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test1/tmp/metadata-sync/ostree-metadata-commits.central b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test1/tmp/metadata-sync/ostree-metadata-commits.central new file mode 100755 index 000000000..05af9e222 --- /dev/null +++ b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test1/tmp/metadata-sync/ostree-metadata-commits.central @@ -0,0 +1,2 @@ +/opt/software/metadata/deployed/starlingx-24.03.0-metadata.xml:- +/opt/software/metadata/deployed/starlingx-24.03.1-metadata.xml:db313865837f9512b024a2356bd76106140ebcea783f8183e5fcc8d5cd28783b diff --git a/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test1/tmp/metadata-sync/ostree-metadata-commits.subcloud b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test1/tmp/metadata-sync/ostree-metadata-commits.subcloud new file mode 100644 index 000000000..a16fa4cf8 --- /dev/null +++ b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test1/tmp/metadata-sync/ostree-metadata-commits.subcloud @@ -0,0 +1 @@ +/opt/software/metadata/deployed/starlingx-24.03.0-metadata.xml:- diff --git a/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/metadata/available/starlingx-24.03.3-metadata.xml b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/metadata/available/starlingx-24.03.3-metadata.xml new file mode 100644 index 000000000..7a699ac7a --- /dev/null +++ b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/metadata/available/starlingx-24.03.3-metadata.xml @@ -0,0 +1,25 @@ + + starlingx-24.03.3 + 24.03.3 + starlingx + Sample inservice test patch + Test + Sample instructions + Sample warning + DEV + N + N + + + starlingx-24.03.0 + + pre-install.sh + post-install.sh + deploy-precheck.sh + + logmgmt_1.0-1.stx.9_all.deb + + 1 + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + diff --git a/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/metadata/deployed/starlingx-24.03.0-metadata.xml b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/metadata/deployed/starlingx-24.03.0-metadata.xml new file mode 100644 index 000000000..eccd5327d --- /dev/null +++ b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/metadata/deployed/starlingx-24.03.0-metadata.xml @@ -0,0 +1,19 @@ + + + starlingx-24.03.0 + 24.03.0 + starlingx + STX 24.03 GA release + STX 24.03 major GA release + + + REL + Y + Y + + + + + + + diff --git a/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/metadata/deployed/starlingx-24.03.1-metadata.xml b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/metadata/deployed/starlingx-24.03.1-metadata.xml new file mode 100644 index 000000000..7fa7b7b7d --- /dev/null +++ b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/metadata/deployed/starlingx-24.03.1-metadata.xml @@ -0,0 +1,27 @@ + + starlingx-24.03.1 + 24.03.1 + starlingx + Sample inservice test patch + This patch DOESN'T require reboot. + This patch should include 1 packages: + - logmgmt + Sample instructions + Sample warning + DEV + N + N + + + starlingx-24.03.0 + + pre-install.sh + post-install.sh + deploy-precheck.sh + + logmgmt_1.0-1.stx.9_all.deb + + 1 + 895ea56dd33882a91a872871f6989c7978ff1413404e2574693380186dbb09a7 + db313865837f9512b024a2356bd76106140ebcea783f8183e5fcc8d5cd28783b + \ No newline at end of file diff --git a/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/metadata/deploying/starlingx-24.03.2-metadata.xml b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/metadata/deploying/starlingx-24.03.2-metadata.xml new file mode 100644 index 000000000..2374fca80 --- /dev/null +++ b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/metadata/deploying/starlingx-24.03.2-metadata.xml @@ -0,0 +1,25 @@ + + starlingx-24.03.2 + 24.03.2 + starlingx + Sample inservice test patch + Test + Sample instructions + Sample warning + DEV + N + N + + + starlingx-24.03.0 + + pre-install.sh + post-install.sh + deploy-precheck.sh + + logmgmt_1.0-1.stx.9_all.deb + + 1 + db313865837f9512b024a2356bd76106140ebcea783f8183e5fcc8d5cd28783b + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + diff --git a/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/tmp/metadata-sync/metadata/available/starlingx-24.03.3-metadata.xml b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/tmp/metadata-sync/metadata/available/starlingx-24.03.3-metadata.xml new file mode 100644 index 000000000..7a699ac7a --- /dev/null +++ b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/tmp/metadata-sync/metadata/available/starlingx-24.03.3-metadata.xml @@ -0,0 +1,25 @@ + + starlingx-24.03.3 + 24.03.3 + starlingx + Sample inservice test patch + Test + Sample instructions + Sample warning + DEV + N + N + + + starlingx-24.03.0 + + pre-install.sh + post-install.sh + deploy-precheck.sh + + logmgmt_1.0-1.stx.9_all.deb + + 1 + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + diff --git a/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/tmp/metadata-sync/metadata/deployed/starlingx-24.03.0-metadata.xml b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/tmp/metadata-sync/metadata/deployed/starlingx-24.03.0-metadata.xml new file mode 100644 index 000000000..eccd5327d --- /dev/null +++ b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/tmp/metadata-sync/metadata/deployed/starlingx-24.03.0-metadata.xml @@ -0,0 +1,19 @@ + + + starlingx-24.03.0 + 24.03.0 + starlingx + STX 24.03 GA release + STX 24.03 major GA release + + + REL + Y + Y + + + + + + + diff --git a/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/tmp/metadata-sync/metadata/deployed/starlingx-24.03.1-metadata.xml b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/tmp/metadata-sync/metadata/deployed/starlingx-24.03.1-metadata.xml new file mode 100644 index 000000000..7fa7b7b7d --- /dev/null +++ b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/tmp/metadata-sync/metadata/deployed/starlingx-24.03.1-metadata.xml @@ -0,0 +1,27 @@ + + starlingx-24.03.1 + 24.03.1 + starlingx + Sample inservice test patch + This patch DOESN'T require reboot. + This patch should include 1 packages: + - logmgmt + Sample instructions + Sample warning + DEV + N + N + + + starlingx-24.03.0 + + pre-install.sh + post-install.sh + deploy-precheck.sh + + logmgmt_1.0-1.stx.9_all.deb + + 1 + 895ea56dd33882a91a872871f6989c7978ff1413404e2574693380186dbb09a7 + db313865837f9512b024a2356bd76106140ebcea783f8183e5fcc8d5cd28783b + \ No newline at end of file diff --git a/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/tmp/metadata-sync/metadata/deploying/starlingx-24.03.2-metadata.xml b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/tmp/metadata-sync/metadata/deploying/starlingx-24.03.2-metadata.xml new file mode 100644 index 000000000..2374fca80 --- /dev/null +++ b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/tmp/metadata-sync/metadata/deploying/starlingx-24.03.2-metadata.xml @@ -0,0 +1,25 @@ + + starlingx-24.03.2 + 24.03.2 + starlingx + Sample inservice test patch + Test + Sample instructions + Sample warning + DEV + N + N + + + starlingx-24.03.0 + + pre-install.sh + post-install.sh + deploy-precheck.sh + + logmgmt_1.0-1.stx.9_all.deb + + 1 + db313865837f9512b024a2356bd76106140ebcea783f8183e5fcc8d5cd28783b + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + diff --git a/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/tmp/metadata-sync/ostree-metadata-commits.central b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/tmp/metadata-sync/ostree-metadata-commits.central new file mode 100755 index 000000000..ec0747e47 --- /dev/null +++ b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/tmp/metadata-sync/ostree-metadata-commits.central @@ -0,0 +1,3 @@ +/opt/software/metadata/deployed/starlingx-24.03.0-metadata.xml:- +/opt/software/metadata/deployed/starlingx-24.03.1-metadata.xml:db313865837f9512b024a2356bd76106140ebcea783f8183e5fcc8d5cd28783b +/opt/software/metadata/deployed/starlingx-24.03.2-metadata.xml:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa diff --git a/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/tmp/metadata-sync/ostree-metadata-commits.subcloud b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/tmp/metadata-sync/ostree-metadata-commits.subcloud new file mode 100644 index 000000000..a16fa4cf8 --- /dev/null +++ b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/metadata/test2/tmp/metadata-sync/ostree-metadata-commits.subcloud @@ -0,0 +1 @@ +/opt/software/metadata/deployed/starlingx-24.03.0-metadata.xml:- diff --git a/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/ostree-metadata-sync.bats b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/ostree-metadata-sync.bats new file mode 100644 index 000000000..9b0112156 --- /dev/null +++ b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/ostree-metadata-sync.bats @@ -0,0 +1,177 @@ +# +# Copyright (c) 2024 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +# vim: set filetype=bash : + +# bats unit tests: https://github.com/bats-core +bats_require_minimum_version 1.5.0 + +# source the script under test +. /code/ostree-metadata-sync.sh + +TEST_METADATA_BASE=/tmp/ostree-metadata-sync-test + +# The setup function is automatically called before each test +setup() { + echo "Running setup" + bats_load_library bats-support + bats_load_library bats-assert + bats_load_library bats-file + + mkdir "${TEST_METADATA_BASE}" || fail 'mkdir failed' + cp -r /code/test/metadata/test1 "${TEST_METADATA_BASE}" || fail 'cp failed' + cp -r /code/test/metadata/test2 "${TEST_METADATA_BASE}" || fail 'cp failed' +} + +init_metadata_dir() { + local test_dir_base=$1 + export METADATA_DIR="${test_dir_base}"/metadata + export METADATA_SYNC_DIR="${test_dir_base}"/tmp/metadata-sync + + export MAJOR_SW_VERSION="24.03" + export MINOR_SW_VERSION="24.03.1" + export SW_VERSION=$MAJOR_SW_VERSION + + export DRY_RUN=1 + + initialize_env +} + +mock_command() { + # usage: mock_command + local command=$1 + shift + eval "export MOCK_OUTPUT_${command}=\"$*\"" + eval "${command}() { echo \"\${MOCK_OUTPUT_${command}}\"; }" +} + +mock_command_exit_code() { + # mocks a command which only returns 0/1 + # usage: mock_command_exit_code + local command=$1 + local exit_code=$2 + eval "${command}() { return ${exit_code}; }" +} + +unmock_command() { + local command=$1 + eval "unset -f ${command}" +} + +# The teardown function runs after each individual test in a file, regardless of test success or failure +teardown() { + if [ -n "${TEST_METADATA_BASE}" ]; then + rm -rf "${TEST_METADATA_BASE}" || fail 'rmdir failed' + fi +} + +@test "test infrastructure and mocking" { + # this is a test of functions defined in bash-template.sh + run log_info "Testing log_info" + assert_output --partial "Testing log_info" + run log_warn "Testing log_warn" + assert_output --partial "Testing log_warn" + run log_progress "Testing log_progress" + assert_output --partial "Testing log_progress" + run log_error "Testing log_error (ignore)" + assert_output --partial "Testing log_error (ignore)" + + mock_command testmock "testing mock" + run testmock + assert_output "testing mock" + unmock_command testmock + run -127 testmock + + mock_command ostree "ostree output" + run ostree + assert_output "ostree output" + unmock_command ostree + run -127 ostree + + mock_command_exit_code testexit 0 + run -0 testexit + unmock_command textexit + run -127 textexit + mock_command_exit_code testexit 1 + run -1 testexit + unmock_command textexit + run -127 textexit +} + +@test "test1 utilities" { + init_metadata_dir "${TEST_METADATA_BASE}"/test1 + + # Test standalone utilities + + local id="starlingx-24.03.0" + local sw_version="24.03.0" + local test_metadata_file="${METADATA_DIR}/deployed/${id}-metadata.xml" + + run find_metadata_files_for_release_sorted "${SW_VERSION}" + assert_output "${test_metadata_file}" + + run get_simple_xml_attrib_from_metadata "${test_metadata_file}" "id" + assert_output "${id}" + run get_simple_xml_attrib_from_metadata "${test_metadata_file}" "sw_version" + assert_output "${sw_version}" + run get_simple_xml_attrib_from_metadata "${test_metadata_file}" "commit" + assert_output "" + + run get_usm_state_from_path "${test_metadata_file}" + assert_output deployed + + run find_metadata_file_for_attrib_val "id" "starlingx-24.03.0" "${METADATA_DIR}" + assert_output "${test_metadata_file}" + + local test_central_metadata_file="${METADATA_SYNC_METADATA_DIR}/deployed/${id}-metadata.xml" + run translate_central_metadata_path "${test_metadata_file}" + assert_output "${test_central_metadata_file}" + + run find_all_ostree_commits_for_release "${SW_VERSION}" + assert_output "${METADATA_DIR}/deployed/starlingx-24.03.0-metadata.xml:-" +} + +@test "test1 data: sync subcloud metadata" { + init_metadata_dir "${TEST_METADATA_BASE}"/test1 + + # mock + mock_command_exit_code pull_ostree_commit_on_subcloud 0 + mock_command_exit_code ostree_commit_exists 0 + run sync_subcloud_metadata + assert_success + + unmock_command pull_ostree_commit_on_subcloud + unmock_command ostree_commit_exists +} + +@test "test2 data: find operations on major release" { + init_metadata_dir "${TEST_METADATA_BASE}"/test2 + + run find_metadata_files_for_release_sorted "${SW_VERSION}" + assert_output "/tmp/ostree-metadata-sync-test/test2/metadata/deployed/starlingx-24.03.0-metadata.xml +/tmp/ostree-metadata-sync-test/test2/metadata/deployed/starlingx-24.03.1-metadata.xml +/tmp/ostree-metadata-sync-test/test2/metadata/deploying/starlingx-24.03.2-metadata.xml +/tmp/ostree-metadata-sync-test/test2/metadata/available/starlingx-24.03.3-metadata.xml" + + run find_all_ostree_commits_for_release "${SW_VERSION}" + assert_output "${METADATA_DIR}/deployed/starlingx-24.03.0-metadata.xml:- +${METADATA_DIR}/deployed/starlingx-24.03.1-metadata.xml:db313865837f9512b024a2356bd76106140ebcea783f8183e5fcc8d5cd28783b +${METADATA_DIR}/deploying/starlingx-24.03.2-metadata.xml:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +${METADATA_DIR}/available/starlingx-24.03.3-metadata.xml:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" +} + +@test "test2 data: find operations on minor release" { + init_metadata_dir "${TEST_METADATA_BASE}"/test2 + + export SW_VERSION=$MINOR_SW_VERSION + + run find_metadata_files_for_release_sorted "${SW_VERSION}" + assert_output "/tmp/ostree-metadata-sync-test/test2/metadata/deployed/starlingx-24.03.1-metadata.xml" + + run find_all_ostree_commits_for_release "${SW_VERSION}" + assert_output "${METADATA_DIR}/deployed/starlingx-24.03.1-metadata.xml:db313865837f9512b024a2356bd76106140ebcea783f8183e5fcc8d5cd28783b" +} + diff --git a/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/run-bats.sh b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/run-bats.sh new file mode 100755 index 000000000..19d8155d8 --- /dev/null +++ b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/files/test/run-bats.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# +# Runs bats (Bash Automated Testing System) https://bats-core.readthedocs.io/ via docker +# + +# shellcheck disable=SC2155 +readonly SCRIPTNAME=$(basename "$0") +# shellcheck disable=SC2155,SC2034 +readonly SCRIPTDIR=$(readlink -m "$(dirname "$0")") + +cd "${SCRIPTDIR}" || { echo "cd failed"; exit 1; } + +# build if necessary +docker images | grep -q 'starlingx/bats' || docker build -t starlingx/bats:latest . + +#echo "Running bats: $(docker run -it bats/bats:latest --help)" +case "$1" in + --help) + "Usage: ${SCRIPTNAME} ?-i|--interactive?" + echo "" + echo "bats --help:" + docker run -it bats/bats:latest --help + ;; + -i|--interactive|--bash|bash|shell) + echo "Running in interactive mode. Run tests in 'test' via: bats test" + docker run -it --rm -v "${PWD}/..:/code" --entrypoint bash starlingx/bats:latest + ;; + *) + docker run -it --rm -v "${PWD}/..:/code" starlingx/bats:latest --verbose-run test/ostree-metadata-sync.bats + ;; +esac diff --git a/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/tasks/main.yml b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/tasks/main.yml new file mode 100644 index 000000000..81ffe120a --- /dev/null +++ b/playbookconfig/src/playbooks/roles/prestage/sync-software-metadata/tasks/main.yml @@ -0,0 +1,209 @@ +--- +# +# Copyright (c) 2024 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# ROLE DESCRIPTION: +# +# This role synchronizes the system controller /opt/software/metadata +# directory to the subcloud. +# +# Note: much of the work is done by the ostree-metadata-sync.sh script, which +# has two targets: get-commits and sync-subcloud +# +# Procedure: +# - On system controller, run ostree-metadata-sync.sh --sw-version 24.09 get-commits +# - Creates /opt/software/tmp/metadata-sync/ostree-metadata-commits.central +# which contains list of sw_version:ostree commit for all +# software updates matching prestaging software release +# - this file is transferred to subcloud for use during the sync-subcloud stage +# - On subcloud, run ostree-metadata-sync.sh --sw-version 24.09 get-commits +# - Creates /opt/software/tmp/metadata-sync/ostree-metadata-commits.subcloud +# which contains list of sw_version:ostree commit for all software +# updates matching prestaging software release +# - Set software_sync_required: +# - The above two get-commits script calls output its list to stdout +# - Based on the stdout diff of the above two script calls, we determine +# if a sync is required +# - Is software_sync_required? +# - Transfer the following from system controller to subcloud at /opt/software/tmp/metadata-sync: +# - /opt/software/tmp/metadata-sync/ostree-metadata-commits.central +# - the full /opt/software/metadata hierarchy +# - The above data is used on the subcloud to synchronize the commits on the subcloud +# by invoking: ostree-metadata-sync.sh --sw-version 24.09 sync-subcloud +# - operates on the data in /opt/software/tmp/metadata-sync +# plus the state on subcloud to run the sync algorithm for each commit +# + +- name: Set software metadata synchronization facts for {{ software_version }} + set_fact: + software_sync_required: false + ostree_feed_repo_dir: /var/www/pages/feed/rel-{{ software_major_release }}/ostree_repo + ostree_sysroot_repo_dir: /sysroot/ostree/repo + ostree_rev: starlingx + usm_software_dir: /opt/software + usm_metadata_dir: /opt/software/metadata + tmp_metadata_sync_dir: /opt/software/tmp/metadata-sync + ostree_metadata_commits_central: /opt/software/tmp/metadata-sync/ostree-metadata-commits.central + ostree_metadata_commits_subcloud: /opt/software/tmp/metadata-sync/ostree-metadata-commits.subcloud + +- name: Gather system controller ostree commit + command: ostree --repo={{ ostree_feed_repo_dir }} rev-parse {{ ostree_rev }} + register: ostree_commit_system_controller + delegate_to: localhost + +- name: Gather subcloud ostree commit + command: ostree --repo={{ ostree_feed_repo_dir }} rev-parse {{ ostree_rev }} + register: ostree_commit_subcloud + +- name: Gather system controller software list + shell: software list --release {{ software_major_release }} | grep {{ software_version }} | grep -E '(available|deploy)' + register: system_controller_software_list + failed_when: false + delegate_to: localhost + +- name: Show system controller software list for release {{ software_version }} + debug: + var: system_controller_software_list.stdout + +- name: Gather subcloud software list + shell: software list --release {{ software_major_release }} | grep {{ software_version }} | grep -E '(available|deploy)' + register: subcloud_software_list + failed_when: false + become: true + +- name: Show subcloud software list for release {{ software_version }} + debug: + var: subcloud_software_list.stdout + +- name: Ensure system controller {{ tmp_metadata_sync_dir }} exists (sysadmin) + file: + path: "{{ tmp_metadata_sync_dir }}" + owner: sysadmin + group: root + state: directory + mode: 0755 + recurse: yes + become: true + delegate_to: localhost + +- name: Ensure subcloud {{ tmp_metadata_sync_dir }} exists (sysadmin) + file: + path: "{{ tmp_metadata_sync_dir }}" + owner: sysadmin + group: root + state: directory + mode: 0755 + recurse: yes + become: true + +- name: Gather system controller metadata commits + # + # Use the existing ostree_metadata_commits_central file if: + # 1) It exists, and + # 2) There are no new commits. Compare last line of the ostree_metadata_commits_central + # file against current ostree repo commit. + # Otherwise, we generate a new ostree_metadata_commits_central file using the + # get-commits target to our script. + # + # Parallel operations (for orchestration): + # We use flock here because there may be many prestaging operations running + # in parallel on system controller. flock behaviour: + # - acquire lock on /tmp/ostree_metadata_commits_central.lock + # - the timeout is long just to ensure we never deadlock for any reason + shell: | + exec 3>/tmp/ostree_metadata_commits_central.lock + flock --exclusive --timeout 180 3 || echo "ERROR: flock failed: $?" + if [ ! -f "{{ ostree_metadata_commits_central }}" ] \ + || diff -q <(ostree --repo="{{ ostree_feed_repo_dir }}" rev-parse "{{ ostree_rev }}") \ + <(tail --lines=1 "{{ ostree_metadata_commits_central }}" | awk -F':' '{ print $2; }'); then + /usr/share/ansible/stx-ansible/playbooks/roles/prestage/sync-software-metadata/files/ostree-metadata-sync.sh \ + --sw-version "{{ software_version }}" --output "{{ ostree_metadata_commits_central }}" get-commits + else + cat "{{ ostree_metadata_commits_central }}" + fi + exec 3>&- # release the lock + register: system_controller_software_metadata_commits + delegate_to: localhost + +- name: Show ostree metadata commits on system controller + debug: + var: system_controller_software_metadata_commits + +- name: Gather subcloud metadata commits + command: >- + /usr/share/ansible/stx-ansible/playbooks/roles/prestage/sync-software-metadata/files/ostree-metadata-sync.sh + --sw-version "{{ software_version }}" --output "{{ ostree_metadata_commits_subcloud }}" get-commits + register: subcloud_software_metadata_commits + +- name: Show ostree metadata commits on subcloud + debug: + var: subcloud_software_metadata_commits + +- name: Decide if software metadata synchronization is required + set_fact: + software_sync_required: true + when: (system_controller_software_metadata_commits.stdout != subcloud_software_metadata_commits.stdout) + or (system_controller_software_list.stdout != subcloud_software_list.stdout) + +- debug: + msg: + - "Software sync required: {{ software_sync_required }}" + - "ostree revision from {{ ostree_feed_repo_dir }}:" + - "system controller: {{ ostree_commit_system_controller.stdout }}" + - "subcloud: {{ ostree_commit_subcloud.stdout }}" + - "Software list:" + - "system controller:" + - "{{ system_controller_software_list.stdout }}" + - "{{ system_controller_software_metadata_commits.stdout }}" + - "subcloud:" + - "{{ subcloud_software_list.stdout }}" + - "{{ subcloud_software_metadata_commits.stdout }}" + +- block: + - name: Copy system controller {{ usm_metadata_dir }} to subcloud {{ tmp_metadata_sync_dir }} + copy: + src: "{{ usm_metadata_dir }}" + dest: "{{ tmp_metadata_sync_dir }}" + force: true + + - name: Copy system controller {{ ostree_metadata_commits_central }} to subcloud {{ tmp_metadata_sync_dir }} + copy: + src: "{{ ostree_metadata_commits_central }}" + dest: "{{ tmp_metadata_sync_dir }}" + force: true + + - name: Synchronizing system controller ostree commits on subcloud + command: >- + /usr/share/ansible/stx-ansible/playbooks/roles/prestage/sync-software-metadata/files/ostree-metadata-sync.sh + --sw-version "{{ software_version }}" sync-subcloud 2>&1 | tee /tmp/sync-ostree-commits.log + register: sync_software_commits + become: true + + - name: Show sync output + debug: + msg: + - "sync_software_commits:" + - "rc: {{ sync_software_commits.rc }}" + - "start: {{ sync_software_commits.start }}" + - "end: {{ sync_software_commits.end }}" + - "stderr: {{ sync_software_commits.stderr }}" + - "stdout: {{ sync_software_commits.stdout }}" + + # Restart the software controller and agent to pickup the changes + - name: Restart the software controller and agent + systemd: + name: "{{ item }}" + state: restarted + with_items: + - software-controller-daemon + - software-agent + become: true + + when: software_sync_required + +- debug: + msg: + - "Skipping software metadata synchronization." + when: not software_sync_required diff --git a/playbookconfig/src/playbooks/validate_host.yml b/playbookconfig/src/playbooks/validate_host.yml index e8442011f..3f8dcd2dc 100644 --- a/playbookconfig/src/playbooks/validate_host.yml +++ b/playbookconfig/src/playbooks/validate_host.yml @@ -7,7 +7,7 @@ - hosts: all # If gathering facts is really necessary, run setup task AFTER host connectivity # check block in prepare-env role. - gather_facts: no + gather_facts: false vars_files: - vars/common/main.yml @@ -19,6 +19,7 @@ check_bootstrap_address: "{{ check_bootstrap_address | default(true) }}" check_patches: "{{ check_patches | default(true) }}" sync_patch_metadata: "{{ sync_patch_metadata | default(false) }}" + sync_software_metadata: "{{ sync_software_metadata | default(false) }}" password_change: "{{ password_change | default(true) }}" password_change_responses: yes/no: 'yes' @@ -32,7 +33,12 @@ roles: - common/prepare-env - role: common/sync-patch-metadata + # TODO turn this off for >= 24.09 targets: when: sync_patch_metadata and os_release == "debian" vars: ansible_become_pass: "{{ ansible_ssh_pass }}" + - role: common/install-sync-software-metadata + when: sync_software_metadata + vars: + ansible_become_pass: "{{ ansible_ssh_pass }}" - common/validate-target diff --git a/tox.ini b/tox.ini index 34b47e945..e46ad3eaa 100644 --- a/tox.ini +++ b/tox.ini @@ -25,7 +25,7 @@ commands = -not -name \*~ \ -not -name \*.md \ -name \*.sh \ - -print0 | xargs -r -n 1 -0 bashate -v" + -print0 | xargs -r -n 1 -0 bashate -iE006 -v" bash -c "find {toxinidir} \ -path '{toxinidir}/.tox' -a -prune \ -o -name '*.yaml' \