| #!/usr/bin/env bash |
| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| |
| #------------------------------------------------------------------------------- |
| # Configuration |
| #------------------------------------------------------------------------------- |
| # This section contains the variables you might want to change. |
| |
| # The temporary directory where files will be downloaded. |
| # It's a good practice to create a unique temporary directory for each script run. |
| TEMP_DIR=$(mktemp -d) |
| |
| # The BASE destination directory for the downloaded image files. |
| # Subdirectories for each distro will be created inside this one. |
| # Make sure this directory exists before running the script. |
| # Must be executed by the cloudstack user on machine hosting the public download site. |
| # It will be publicly available at https://download.cloudstack.org/templates/cloud-images/ |
| DEST_DIR="${HOME}/repository/templates/cloud-images" |
| |
| # The directory where log files will be stored. |
| # Make sure this directory exists. |
| LOG_DIR="${HOME}/log/cloud-image-downloader" |
| LOG_FILE="${LOG_DIR}/cloud-image-downloader_$(date +%Y%m%d_%H%M%S).log" |
| LOG_RETENTION_DAYS=30 |
| |
| LOGGER_TAG="cloud-image-downloader" |
| LOGGER_FACILITY="user" |
| LOGGER_AVAILABLE=false |
| |
| log_message() { |
| local priority=$1 |
| shift |
| local message="$*" |
| local timestamp=$(date +'%Y-%m-%d %H:%M:%S') |
| |
| # Log to file |
| echo "${timestamp} [${priority}] ${message}" | tee -a "${LOG_FILE}" |
| |
| # Log to syslog using logger utility |
| if [ "${LOGGER_AVAILABLE}" = true ]; then |
| logger -t "${LOGGER_TAG}" -p "${LOGGER_FACILITY}.${priority}" -- "${message}" |
| fi |
| } |
| |
| log_info() { |
| log_message "info" "$@" |
| } |
| |
| log_warn() { |
| log_message "warning" "$@" |
| } |
| |
| log_error() { |
| log_message "err" "$@" |
| } |
| |
| cleanup_old_logs() { |
| log_info "Cleaning up log files older than ${LOG_RETENTION_DAYS} days..." |
| |
| if [ ! -d "$LOG_DIR" ]; then |
| log_warn "Log directory does not exist: $LOG_DIR" |
| return |
| fi |
| |
| local deleted_count=0 |
| |
| # Find and delete log files older than retention period |
| while IFS= read -r -d '' log_file; do |
| rm -f "$log_file" |
| deleted_count=$((deleted_count + 1)) |
| done < <(find "$LOG_DIR" -name "*.log" -type f -mtime +${LOG_RETENTION_DAYS} -print0 2>/dev/null) |
| |
| if [ $deleted_count -gt 0 ]; then |
| log_info "Deleted $deleted_count old log file(s)" |
| else |
| log_info "No old log files to delete" |
| fi |
| } |
| |
| #------------------------------------------------------------------------------- |
| # Image Definitions |
| #------------------------------------------------------------------------------- |
| # To add a new image, you must add an entry to BOTH arrays below. |
| |
| # 1. Add the destination filename and the download URL. |
| declare -A IMAGE_URLS=( |
| ["Rocky-9-GenericCloud.latest.x86_64.qcow2"]="https://dl.rockylinux.org/pub/rocky/9/images/x86_64/Rocky-9-GenericCloud.latest.x86_64.qcow2" |
| ["Rocky-9-GenericCloud.latest.aarch64.qcow2"]="https://dl.rockylinux.org/pub/rocky/9/images/aarch64/Rocky-9-GenericCloud.latest.aarch64.qcow2" |
| ["Rocky-8-GenericCloud.latest.x86_64.qcow2"]="https://dl.rockylinux.org/pub/rocky/8/images/x86_64/Rocky-8-GenericCloud.latest.x86_64.qcow2" |
| ["Rocky-8-GenericCloud.latest.aarch64.qcow2"]="https://dl.rockylinux.org/pub/rocky/8/images/aarch64/Rocky-8-GenericCloud.latest.aarch64.qcow2" |
| ["openSUSE-Leap-15.5-Minimal-VM.x86_64-Cloud.qcow2"]="https://download.opensuse.org/distribution/leap/15.5/appliances/openSUSE-Leap-15.5-Minimal-VM.x86_64-Cloud.qcow2" |
| ["openSUSE-Leap-15.5-Minimal-VM.aarch64-Cloud.qcow2"]="https://download.opensuse.org/distribution/leap/15.5/appliances/openSUSE-Leap-15.5-Minimal-VM.aarch64-Cloud.qcow2" |
| ["debian-12-genericcloud-amd64.qcow2"]="https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2" |
| ["debian-12-genericcloud-arm64.qcow2"]="https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-arm64.qcow2" |
| ["ubuntu-24.04-server-cloudimg-amd64.img"]="https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img" |
| ["ubuntu-24.04-server-cloudimg-arm64.img"]="https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-arm64.img" |
| ["ubuntu-22.04-server-cloudimg-amd64.img"]="https://cloud-images.ubuntu.com/releases/22.04/release/ubuntu-22.04-server-cloudimg-amd64.img" |
| ["ubuntu-22.04-server-cloudimg-arm64.img"]="https://cloud-images.ubuntu.com/releases/22.04/release/ubuntu-22.04-server-cloudimg-arm64.img" |
| ["ubuntu-20.04-server-cloudimg-amd64.img"]="https://cloud-images.ubuntu.com/releases/20.04/release/ubuntu-20.04-server-cloudimg-amd64.img" |
| ["ubuntu-20.04-server-cloudimg-arm64.img"]="https://cloud-images.ubuntu.com/releases/20.04/release/ubuntu-20.04-server-cloudimg-arm64.img" |
| ["OL9U5_x86_64-kvm-b259.qcow2"]="https://yum.oracle.com/templates/OracleLinux/OL9/u5/x86_64/OL9U5_x86_64-kvm-b259.qcow2" |
| ["OL9U5_aarch64-kvm-b126.qcow2"]="https://yum.oracle.com/templates/OracleLinux/OL9/u5/aarch64/OL9U5_aarch64-kvm-b126.qcow2" |
| ["OL8U10_x86_64-kvm-b258.qcow2"]="https://yum.oracle.com/templates/OracleLinux/OL8/u10/x86_64/OL8U10_x86_64-kvm-b258.qcow2" |
| ["OL8U10_aarch64-kvm-b122.qcow2"]="https://yum.oracle.com/templates/OracleLinux/OL8/u10/aarch64/OL8U10_aarch64-kvm-b122.qcow2" |
| ) |
| |
| # 2. Add the destination filename and its corresponding distribution subdirectory name. |
| declare -A IMAGE_DISTROS=( |
| ["Rocky-9-GenericCloud.latest.x86_64.qcow2"]="rockylinux" |
| ["Rocky-9-GenericCloud.latest.aarch64.qcow2"]="rockylinux" |
| ["Rocky-8-GenericCloud.latest.x86_64.qcow2"]="rockylinux" |
| ["Rocky-8-GenericCloud.latest.aarch64.qcow2"]="rockylinux" |
| ["openSUSE-Leap-15.5-Minimal-VM.x86_64-Cloud.qcow2"]="opensuse" |
| ["openSUSE-Leap-15.5-Minimal-VM.aarch64-Cloud.qcow2"]="opensuse" |
| ["debian-12-genericcloud-amd64.qcow2"]="debian" |
| ["debian-12-genericcloud-arm64.qcow2"]="debian" |
| ["ubuntu-24.04-server-cloudimg-amd64.img"]="ubuntu" |
| ["ubuntu-24.04-server-cloudimg-arm64.img"]="ubuntu" |
| ["ubuntu-22.04-server-cloudimg-amd64.img"]="ubuntu" |
| ["ubuntu-22.04-server-cloudimg-arm64.img"]="ubuntu" |
| ["ubuntu-20.04-server-cloudimg-amd64.img"]="ubuntu" |
| ["ubuntu-20.04-server-cloudimg-arm64.img"]="ubuntu" |
| ["OL9U5_x86_64-kvm-b259.qcow2"]="oraclelinux" |
| ["OL9U5_aarch64-kvm-b126.qcow2"]="oraclelinux" |
| ["OL8U10_x86_64-kvm-b258.qcow2"]="oraclelinux" |
| ["OL8U10_aarch64-kvm-b122.qcow2"]="oraclelinux" |
| ) |
| |
| #------------------------------------------------------------------------------- |
| # Cleanup Handler |
| #------------------------------------------------------------------------------- |
| |
| cleanup_on_exit() { |
| local exit_code=$? |
| if [ -d "$TEMP_DIR" ]; then |
| rm -rf "$TEMP_DIR" |
| log_info "Temporary directory $TEMP_DIR removed." |
| fi |
| |
| if [ $exit_code -ne 0 ]; then |
| log_error "Script exited with error code: $exit_code" |
| fi |
| } |
| |
| trap cleanup_on_exit EXIT INT TERM |
| |
| #------------------------------------------------------------------------------- |
| # Main Script Logic |
| #------------------------------------------------------------------------------- |
| |
| if command -v logger &> /dev/null; then |
| LOGGER_AVAILABLE=true |
| fi |
| |
| # Ensure base destination and log directories exist |
| mkdir -p "$DEST_DIR" |
| mkdir -p "$LOG_DIR" |
| |
| # Clean up old logs first |
| cleanup_old_logs |
| |
| log_info "Starting image download process." |
| log_info "Temporary directory: $TEMP_DIR" |
| log_info "Base destination directory: $DEST_DIR" |
| log_info "Log file: $LOG_FILE" |
| |
| # Inform about logger status |
| if [ "${LOGGER_AVAILABLE}" = true ]; then |
| log_info "Syslog logging enabled (tag: ${LOGGER_TAG})" |
| else |
| log_warn "Syslog logging disabled - logger utility not found" |
| fi |
| |
| # Loop through the image URLs |
| for filename in "${!IMAGE_URLS[@]}"; do |
| url="${IMAGE_URLS[$filename]}" |
| distro="${IMAGE_DISTROS[$filename]}" |
| |
| # Check if a distro is defined for the file |
| if [ -z "$distro" ]; then |
| log_error "No distribution directory defined for $filename. Skipping." |
| continue |
| fi |
| |
| distro_dest_dir="${DEST_DIR}/${distro}" |
| temp_filepath="${TEMP_DIR}/${filename}" |
| dest_filepath="${distro_dest_dir}/${filename}" |
| |
| log_info "--------------------------------------------------" |
| log_info "Starting download for: $filename" |
| log_info "URL: $url" |
| |
| # Download the file to the temporary directory |
| wget --progress=bar:force:noscroll -O "$temp_filepath" "$url" |
| download_status=$? |
| |
| if [ $download_status -ne 0 ]; then |
| # Handle download failure |
| log_error "Failed to download $filename from $url. wget exit code: $download_status" |
| else |
| # Handle download success |
| log_info "Successfully downloaded $filename to temporary location." |
| |
| # Ensure the specific distro directory exists |
| log_info "Ensuring destination directory exists: $distro_dest_dir" |
| mkdir -p "$distro_dest_dir" |
| |
| # Move the file to the destination directory, replacing any existing file |
| log_info "Moving $filename to $dest_filepath" |
| mv -f "$temp_filepath" "$dest_filepath" |
| move_status=$? |
| |
| if [ $move_status -ne 0 ]; then |
| log_error "Failed to move $filename to $dest_filepath. mv exit code: $move_status" |
| else |
| log_info "Successfully moved $filename." |
| fi |
| fi |
| done |
| |
| log_info "Generate checksum" |
| # Create md5 checksum |
| checksum_file="md5sum.txt" |
| sha512_checksum_file="sha512sum.txt" |
| |
| cd "$DEST_DIR" |
| find . -type f ! -iname '*.txt' -exec md5sum {} \; > "$checksum_file" |
| checksum_status=$? |
| if [ $checksum_status -ne 0 ]; then |
| log_error "Failed to create md5 checksum. md5sum exit code: $checksum_status" |
| else |
| log_info "Successfully created checksum file: $checksum_file" |
| fi |
| |
| find . -type f ! -iname '*.txt' -exec sha512sum {} \; > "$sha512_checksum_file" |
| sha512_checksum_status=$? |
| if [ $sha512_checksum_status -ne 0 ]; then |
| log_error "Failed to create sha512 checksum. sha512sum exit code: $sha512_checksum_status" |
| else |
| log_info "Successfully created checksum file: $sha512_checksum_file" |
| fi |
| |
| log_info "--------------------------------------------------" |
| log_info "Image download process finished." |