Add Proxmox Container Update Script and update README

This commit is contained in:
Enrico Ludwig 2025-04-03 21:22:03 +02:00
parent 5b41b49ed4
commit 4d08911bdc
3 changed files with 639 additions and 0 deletions

View File

@ -12,6 +12,7 @@ We're planning to release a growing amount of open source software, that is free
| Name | Description | License | Current Version | Written in | Supported Distros | Path |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | --------------- | ---------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| Proxmox VE Container Update Helper | Simplify updating your Proxmox VE containers and perform updates or upgrades on either all or specified containers. You can include and exclude specific container IDs. | [MIT]([LICENSE](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/LICENSE)) | v1.0.0 | Bash | Proxmox VE Debian | [Proxmox VE PCT Update Helper](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/proxmox/)
| Borgmatic Setup Tool | If you plan to use borg as backup solution, you should also take a look at [borgmatic](https://torsion.org/borgmatic/). It's a Python wrapper for the award winning backup tool [borgbackup](https://borgbackup.readthedocs.io/en/stable/index.html) that simplifies creating secure and reliable backups even more. You can even store your configurations in files. This script will do the setup for you to. | [MIT]([LICENSE](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/LICENSE)) | v1.2.1 | Bash | Debian and derivates | [Borgmatic Setup](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/borgmatic/) |
| Git Rewrite Author | **USE WITH CAUTION!!!**<br><br> This script will rewrite the entire history of the remote end and set the author email to the provided one.<br><br>**This is NOT reversible!** | [MIT]([LICENSE](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/LICENSE)) | v1.0.0 | Bash | Most Linux distros | [Git Scripts](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/git/) |
| UFW Beautifier | Simple Python script to get a fancy formatted `ufw.log` | [MIT]([LICENSE](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/LICENSE)) | v1.0.0 | Python | Most Linux distros | [UFW Beautifier](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/ufw/) |

128
proxmox/README.md Normal file
View File

@ -0,0 +1,128 @@
# 📦 Proxmox Container Update Script
A powerful utility script for managing updates across multiple Proxmox containers with flexible options and detailed logging.
## 🔍 Overview
`pct_update.sh` simplifies the maintenance of Proxmox containers by providing an easy way to check, update, and upgrade packages across multiple containers simultaneously. It supports various Linux distributions and offers extensive customization options for handling container updates efficiently.
## ✨ Features
- **Status Checking**: Quickly identify containers with pending updates
- **Package Management**: Update package caches or perform full system upgrades
- **Selective Processing**: Include or exclude specific containers by ID
- **Container Control**: Boot non-running containers before operations
- **Safety Options**: Dry-run mode to test without making changes
- **Automation Support**: Non-interactive mode for scheduled tasks
- **Comprehensive Logging**: Color-coded logs with adjustable verbosity
- **OS Support**: Works with Debian, Ubuntu, Alpine, CentOS, Fedora, and RHEL
## 🚀 Installation
1. Clone the repository or download the script
2. Make the script executable:
```bash
chmod +x pct_update.sh
```
3. Run the script with appropriate options (see Usage)
Optionally you can save the script to `/usr/local/sbin` to make it available system wide:
```bash
sudo cp pct_update.sh /usr/local/sbin/pct_update
sudo chmod +x /usr/local/sbin/pct_update
```
## 🛠️ Usage
```bash
./pct_update.sh [options]
```
(or, if globally installed)
```bash
pct_update [options]
```
### ⚙️ Options
- `-s` : Get the package status of each container.
- `-u` : Update the package cache, but do not upgrade.
- `-U` : Perform upgrades on all containers.
- `-x <ID>` : Exclude a container by its ID (can be used multiple times).
- `-i <ID>` : Include only specific containers by their IDs (can be used multiple times).
- `-y` : Assume 'yes' for all operations (asks once).
- `-Y` : Assume 'yes' for all operations without asking (for automation).
- `-b` : Boot non-running containers before performing actions.
- `-B <ID>` : Boot a specific container by its ID (can be used multiple times).
- `-d` : Dry-run mode (simulate actions without making changes).
- `-v` : Enable verbose mode.
- `-h` : Print this help and exit.
## 💻 Prerequisites
Ensure the following commands are available on your system:
- `awk`
- `grep`
- `pct` (Proxmox Container Toolkit)
## 🚨 Warnings
- Running as root: The script will warn if executed as root. Ensure this is necessary for your operations.
- Conflicts: Avoid using both `-b` and `-B` options simultaneously, as they may lead to unintended behavior.
## 📋 Examples
- **Check package status for all containers**:
```bash
./pct_update.sh -s
```
- **Update package cache for specific containers**:
```bash
./pct_update.sh -u -i 101 -i 102
```
- **Upgrade all containers, excluding some**:
```bash
./pct_update.sh -U -x 103 -x 104
```
- **Dry-run an upgrade for all containers**:
```bash
./pct_update.sh -Ud
```
- **Boot a specific container before upgrade**:
```bash
./pct_update.sh -U -B 105
```
- **Update all containers without asking for confirmation**:
```bash
./pct_update.sh -YU
```
## 📝 Logging
The script provides detailed logging for each operation, categorized by:
- **DEBUG**: Detailed information for debugging.
- **INFO**: General information about the operations.
- **WARNING**: Potential issues that do not stop the script.
- **ERROR**: Critical issues that prevent operations.
## 🤝 Contributions
Contributions are welcome! Feel free to open issues or submit pull requests to improve this script.
## 📄 License
This project is licensed under the [MIT License](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/LICENSE).
---
Happy updating! 🎉

510
proxmox/pct_update.sh Executable file
View File

@ -0,0 +1,510 @@
#!/bin/bash
# Define color variables for formatted output
COLOR_DEBUG="\033[0;36m" # Cyan
COLOR_INFO="\033[0;32m" # Green
COLOR_WARNING="\033[0;33m" # Yellow
COLOR_ERROR="\033[0;31m" # Red
COLOR_DRY_RUN="\033[0;35m" # Purple
COLOR_RESET="\033[0m" # Reset to default
# Color Codes for all colors
COLOR_BLACK="\033[0;30m"
COLOR_RED="\033[0;31m"
COLOR_GREEN="\033[0;32m"
COLOR_YELLOW="\033[0;33m"
COLOR_BLUE="\033[0;34m"
COLOR_PURPLE="\033[0;35m"
COLOR_CYAN="\033[0;36m"
COLOR_WHITE="\033[0;37m"
# Color Codes for all colors with background
COLOR_BLACK_BG="\033[0;40m"
COLOR_RED_BG="\033[0;41m"
COLOR_GREEN_BG="\033[0;42m"
COLOR_YELLOW_BG="\033[0;43m"
COLOR_BLUE_BG="\033[0;44m"
COLOR_PURPLE_BG="\033[0;45m"
COLOR_CYAN_BG="\033[0;46m"
COLOR_WHITE_BG="\033[0;47m"
# Formatting Codes
BOLD="\033[1m"
DIM="\033[2m"
UNDERLINED="\033[4m"
BLINK="\033[5m"
INVERTED="\033[7m"
HIDDEN="\033[8m"
# Combined Formatting Codes
BOLD_BLACK="\033[1;30m"
BOLD_RED="\033[1;31m"
BOLD_GREEN="\033[1;32m"
BOLD_YELLOW="\033[1;33m"
BOLD_BLUE="\033[1;34m"
BOLD_PURPLE="\033[1;35m"
BOLD_CYAN="\033[1;36m"
BOLD_WHITE="\033[1;37m"
DIM_BLACK="\033[2;30m"
DIM_RED="\033[2;31m"
DIM_GREEN="\033[2;32m"
DIM_YELLOW="\033[2;33m"
DIM_BLUE="\033[2;34m"
DIM_PURPLE="\033[2;35m"
DIM_CYAN="\033[2;36m"
DIM_WHITE="\033[2;37m"
UNDERLINED_BLACK="\033[4;30m"
UNDERLINED_RED="\033[4;31m"
UNDERLINED_GREEN="\033[4;32m"
UNDERLINED_YELLOW="\033[4;33m"
UNDERLINED_BLUE="\033[4;34m"
UNDERLINED_PURPLE="\033[4;35m"
UNDERLINED_CYAN="\033[4;36m"
UNDERLINED_WHITE="\033[4;37m"
BLINK_BLACK="\033[5;30m"
BLINK_RED="\033[5;31m"
BLINK_GREEN="\033[5;32m"
BLINK_YELLOW="\033[5;33m"
BLINK_BLUE="\033[5;34m"
BLINK_PURPLE="\033[5;35m"
BLINK_CYAN="\033[5;36m"
BLINK_WHITE="\033[5;37m"
INVERTED_BLACK="\033[7;30m"
INVERTED_RED="\033[7;31m"
INVERTED_GREEN="\033[7;32m"
INVERTED_YELLOW="\033[7;33m"
INVERTED_BLUE="\033[7;34m"
INVERTED_PURPLE="\033[7;35m"
INVERTED_CYAN="\033[7;36m"
INVERTED_WHITE="\033[7;37m"
HIDDEN_BLACK="\033[8;30m"
HIDDEN_RED="\033[8;31m"
HIDDEN_GREEN="\033[8;32m"
HIDDEN_YELLOW="\033[8;33m"
HIDDEN_BLUE="\033[8;34m"
HIDDEN_PURPLE="\033[8;35m"
HIDDEN_CYAN="\033[8;36m"
HIDDEN_WHITE="\033[8;37m"
BOLD_DIM_BLACK="\033[1;2;30m"
BOLD_DIM_RED="\033[1;2;31m"
BOLD_DIM_GREEN="\033[1;2;32m"
BOLD_DIM_YELLOW="\033[1;2;33m"
BOLD_DIM_BLUE="\033[1;2;34m"
BOLD_DIM_PURPLE="\033[1;2;35m"
BOLD_DIM_CYAN="\033[1;2;36m"
BOLD_DIM_WHITE="\033[1;2;37m"
BOLD_UNDERLINED_BLACK="\033[1;4;30m"
BOLD_UNDERLINED_RED="\033[1;4;31m"
BOLD_UNDERLINED_GREEN="\033[1;4;32m"
BOLD_UNDERLINED_YELLOW="\033[1;4;33m"
BOLD_UNDERLINED_BLUE="\033[1;4;34m"
BOLD_UNDERLINED_PURPLE="\033[1;4;35m"
BOLD_UNDERLINED_CYAN="\033[1;4;36m"
BOLD_UNDERLINED_WHITE="\033[1;4;37m"
BOLD_BLINK_BLACK="\033[1;5;30m"
BOLD_BLINK_RED="\033[1;5;31m"
BOLD_BLINK_GREEN="\033[1;5;32m"
BOLD_BLINK_YELLOW="\033[1;5;33m"
BOLD_BLINK_BLUE="\033[1;5;34m"
BOLD_BLINK_PURPLE="\033[1;5;35m"
BOLD_BLINK_CYAN="\033[1;5;36m"
BOLD_BLINK_WHITE="\033[1;5;37m"
DIM_UNDERLINED_BLACK="\033[2;4;30m"
DIM_UNDERLINED_RED="\033[2;4;31m"
DIM_UNDERLINED_GREEN="\033[2;4;32m"
DIM_UNDERLINED_YELLOW="\033[2;4;33m"
DIM_UNDERLINED_BLUE="\033[2;4;34m"
DIM_UNDERLINED_PURPLE="\033[2;4;35m"
DIM_UNDERLINED_CYAN="\033[2;4;36m"
DIM_UNDERLINED_WHITE="\033[2;4;37m"
DIM_BLINK_BLACK="\033[2;5;30m"
DIM_BLINK_RED="\033[2;5;31m"
DIM_BLINK_GREEN="\033[2;5;32m"
DIM_BLINK_YELLOW="\033[2;5;33m"
DIM_BLINK_BLUE="\033[2;5;34m"
DIM_BLINK_PURPLE="\033[2;5;35m"
DIM_BLINK_CYAN="\033[2;5;36m"
DIM_BLINK_WHITE="\033[2;5;37m"
BOLD_DIM_UNDERLINED_BLACK="\033[1;2;4;30m"
BOLD_DIM_UNDERLINED_RED="\033[1;2;4;31m"
BOLD_DIM_UNDERLINED_GREEN="\033[1;2;4;32m"
BOLD_DIM_UNDERLINED_YELLOW="\033[1;2;4;33m"
BOLD_DIM_UNDERLINED_BLUE="\033[1;2;4;34m"
BOLD_DIM_UNDERLINED_PURPLE="\033[1;2;4;35m"
BOLD_DIM_UNDERLINED_CYAN="\033[1;2;4;36m"
BOLD_DIM_UNDERLINED_WHITE="\033[1;2;4;37m"
# Function to print help
print_help() {
echo "Usage: $0 [options]"
echo "Options:"
echo " -s Get the package status of each container"
echo " -u Update the package cache, but do not upgrade"
echo " -U Perform upgrades on all containers"
echo " -x <ID> Exclude a container by its ID (can be used multiple times)"
echo " -i <ID> Include only specific containers by their IDs (can be used multiple times)"
echo " -y Assume 'yes' for all operations (asks once)"
echo " -Y Assume 'yes' for all operations without asking (for automation)"
echo " -b Boot non-running containers before performing actions"
echo " -B <ID> Boot a specific container by its ID (can be used multiple times)"
echo " -d Dry-run mode (simulate actions without making changes)"
echo " -v Enable verbose mode"
echo " -h Print this help and exit"
}
# Logging functions
log_dbg() { [ "$VERBOSE" = true ] && echo -e "${COLOR_DEBUG}[$(date '+%Y-%m-%d %H:%M:%S')][DEBUG] $1${COLOR_RESET}"; }
log_inf() { echo -e "${COLOR_INFO}[$(date '+%Y-%m-%d %H:%M:%S')][INFO] $1${COLOR_RESET}"; }
log_wrn() { echo -e "${COLOR_WARNING}[$(date '+%Y-%m-%d %H:%M:%S')][WARNING] $1${COLOR_RESET}"; }
log_err() { echo -e "${COLOR_ERROR}[$(date '+%Y-%m-%d %H:%M:%S')][ERROR] $1${COLOR_RESET}"; }
log_dry_run() { echo -e "${COLOR_DRY_RUN}[DRY-RUN] $1${COLOR_RESET}"; }
# Check for required commands
for cmd in awk grep pct; do
if ! command -v $cmd &> /dev/null; then
log_err "Required command $cmd is not available."
exit 1
fi
done
# Warn if running as root
if [ "$(id -u)" -eq 0 ]; then
log_wrn "Running as root. Ensure this is necessary for your operations."
fi
# Initialize variables
EXCLUDE_IDS=()
INCLUDE_IDS=()
BOOT_IDS=()
ACTION=""
ASSUME_YES=false
FORCE_YES=false
BOOT_CONTAINERS=false
DRY_RUN=false
VERBOSE=false
# Internal variables
STATUS_SUMMARIES=()
# Parse arguments
while getopts ":suUx:i:hyYbdvB:" opt; do
case ${opt} in
s|u|U)
if [[ -n "$ACTION" && "$ACTION" != "$opt" ]]; then
log_err "Multiple actions specified. Please specify only one of -s, -u, or -U."
exit 1
fi
ACTION="$opt"
;;
x) EXCLUDE_IDS+=("$OPTARG") ;;
i) INCLUDE_IDS+=("$OPTARG") ;;
y) ASSUME_YES=true ;;
Y) FORCE_YES=true ;;
b) BOOT_CONTAINERS=true ;;
B) BOOT_IDS+=("$OPTARG") ;;
d) DRY_RUN=true ;;
v) VERBOSE=true ;;
h) print_help; exit 0 ;;
\?) log_err "Invalid option: -$OPTARG"; exit 1 ;;
:) log_err "Option -$OPTARG requires an argument."; exit 1 ;;
esac
done
# Check for conflicts between -b and -B
if [ "$BOOT_CONTAINERS" = true ] && [ ${#BOOT_IDS[@]} -gt 0 ]; then
log_err "Conflict: Both -b and -B options are set. Use only one. -b will boot all containers, which may not be intended."
exit 1
fi
# Check if an action is specified
if [ -z "$ACTION" ]; then
log_err "No action specified. Use -h for help."
exit 1
fi
# Check for conflicts between include and exclude lists
for id in "${INCLUDE_IDS[@]}"; do
if [[ " ${EXCLUDE_IDS[@]} " =~ " $id " ]]; then
log_err "Container ID $id is both included and excluded. This is not allowed."
exit 1
fi
done
# Get list of all containers
log_inf "Retrieving list of containers..."
CONTAINERS=$(pct list | awk 'NR>1 {print $1}' | grep -E '^[0-9]+$')
log_dbg "Containers found: $CONTAINERS"
# Function to check if a container ID is in the exclude list
is_excluded() {
local id="$1"
for exclude_id in "${EXCLUDE_IDS[@]}"; do
if [ "$exclude_id" == "$id" ]; then
return 0
fi
done
return 1
}
# Function to check if a container ID is in the include list
is_included() {
local id="$1"
if [ ${#INCLUDE_IDS[@]} -eq 0 ]; then
return 0
fi
for include_id in "${INCLUDE_IDS[@]}"; do
if [ "$include_id" == "$id" ]; then
return 0
fi
done
return 1
}
# Function to check if a container ID is in the boot list
is_booted() {
local id="$1"
for boot_id in "${BOOT_IDS[@]}"; do
if [ "$boot_id" == "$id" ]; then
return 0
fi
done
return 1
}
# Function to confirm actions
confirm_action() {
local message="$1"
if $FORCE_YES; then
return 0
elif $ASSUME_YES; then
log_wrn "Assuming 'yes' for all operations. Proceed with caution."
return 0
fi
read -p "$message (y/n): " choice
case "$choice" in
y|Y ) return 0 ;;
* ) return 1 ;;
esac
}
# Function to validate container ID
validate_container_id() {
local id="$1"
if ! [[ "$id" =~ ^[0-9]+$ ]]; then
log_err "Invalid container ID: $id"
return 1
fi
return 0
}
# Function to check if a container ID exists
container_exists() {
local id="$1"
if pct list | awk 'NR>1 {print $1}' | grep -q "^$id$"; then
return 0
else
log_err "Container ID $id does not exist."
return 1
fi
}
# Function to check if there are enough resources to boot a container
can_boot_container() {
local id="$1"
# Example check: Ensure there is enough free memory (this is a placeholder and should be replaced with actual checks)
local required_memory=$(pct config "$id" | grep -i "memory" | awk '{print $2}')
local free_memory=$(free -m | awk '/^Mem:/{print $7}')
if [ "$free_memory" -lt "$required_memory" ]; then
log_err "Not enough memory to boot container $id. Required: $required_memory MB, Available: $free_memory MB."
return 1
fi
return 0
}
color() {
local color="$1"
local message="$2"
local previous_color="${3:-$COLOR_RESET}"
return "${color}$message${previous_color}"
}
# Filter containers based on include/exclude lists
FILTERED_CONTAINERS=()
for CTID in $CONTAINERS; do
if ! validate_container_id "$CTID"; then
continue
fi
if ! container_exists "$CTID"; then
continue
fi
if is_excluded "$CTID"; then
continue
fi
if is_included "$CTID"; then
FILTERED_CONTAINERS+=("$CTID")
fi
done
# Check if there are any containers to process
if [ ${#FILTERED_CONTAINERS[@]} -eq 0 ]; then
log_wrn "No containers match the specified criteria."
exit 0
fi
# Iterate over each filtered container
for CTID in "${FILTERED_CONTAINERS[@]}"; do
log_dbg "Processing container ID: $CTID"
# Check if the container is running
STATUS=$(pct status "$CTID" 2>/dev/null | awk '{print $2}')
if [ $? -ne 0 ]; then
log_err "Failed to retrieve status for container $CTID."
continue
fi
log_dbg "Container $CTID status: $STATUS"
if [ "$STATUS" != "running" ]; then
if $BOOT_CONTAINERS || is_booted "$CTID"; then
log_inf "Container $CTID is not running."
if $DRY_RUN; then
log_dry_run "Would boot container $CTID."
else
if can_boot_container "$CTID"; then
if confirm_action "Boot container $CTID?"; then
if pct start "$CTID"; then
log_inf "Container $CTID started."
else
log_err "Failed to start container $CTID."
continue
fi
else
log_wrn "Boot skipped for container $CTID."
continue
fi
else
log_wrn "Insufficient resources to boot container $CTID."
continue
fi
fi
else
log_wrn "Container $CTID is not running and will be skipped."
continue
fi
fi
# Get the OS of the container
OS=$(pct config "$CTID" | grep -i "ostype" | awk '{print $2}' | tr '[:upper:]' '[:lower:]')
CTNAME=$(pct config "$CTID" | grep -i "hostname" | awk '{print $2}')
log_dbg "Container $CTID OS: $OS, Hostname: $CTNAME"
# Determine package manager and commands based on OS
case "$OS" in
debian|ubuntu)
PKG_STATUS_CMD="apt list --upgradable 2>/dev/null | grep -v 'Listing' | wc -l"
UPDATE_CMD="apt update"
UPGRADE_CMD="apt upgrade -y"
;;
alpine)
PKG_STATUS_CMD="apk version -l '<' | wc -l"
UPDATE_CMD="apk update"
UPGRADE_CMD="apk upgrade -y"
;;
centos|fedora|rhel)
if pct exec "$CTID" -- /bin/sh -c "command -v dnf" &> /dev/null; then
PKG_STATUS_CMD="dnf check-update | wc -l"
UPDATE_CMD="dnf makecache"
UPGRADE_CMD="dnf upgrade -y"
else
PKG_STATUS_CMD="yum check-update | wc -l"
UPDATE_CMD="yum makecache"
UPGRADE_CMD="yum upgrade -y"
fi
;;
*)
log_err "Unsupported OS for container $CTID: $OS"
continue
;;
esac
log_dbg "Package status command: $PKG_STATUS_CMD"
log_dbg "Update command: $UPDATE_CMD"
log_dbg "Upgrade command: $UPGRADE_CMD"
# Execute the appropriate action
case "$ACTION" in
s)
log_inf "Checking package status for container $CTNAME ($CTID)..."
if $DRY_RUN; then
log_dry_run "Would check package status for container $CTNAME ($CTID)."
else
UPDATES=$(pct exec "$CTID" -- /bin/sh -c "$PKG_STATUS_CMD")
if [ $? -ne 0 ]; then
log_err "Failed to check package status for container $CTNAME ($CTID)."
continue
fi
log_dbg "Updates available for container $CTNAME ($CTID): $UPDATES"
if [ "$UPDATES" -gt 0 ]; then
log_inf "Container $CTNAME ($CTID) STATUS: \033[1mHas Updates ($UPDATES)\033[0m"
else
log_dbg "Container $CTNAME ($CTID) STATUS: No Updates available"
fi
# Save the summary for later
STATUS_SUMMARIES+=("$CTNAME ($CTID): $UPDATES")
fi
;;
u)
log_inf "Preparing to update package cache for container $CTNAME ($CTID)..."
if $DRY_RUN; then
log_dry_run "Would execute update on $CTNAME ($CTID) with command: $UPDATE_CMD"
else
if confirm_action "Execute update on $CTNAME ($CTID) with command: $UPDATE_CMD"; then
if pct exec "$CTID" -- /bin/sh -c "$UPDATE_CMD"; then
log_inf "Update completed for $CTNAME ($CTID)"
else
log_err "Update failed for $CTNAME ($CTID)"
continue
fi
else
log_wrn "Update skipped for $CTNAME ($CTID)"
fi
fi
;;
U)
log_inf "Preparing to upgrade packages for container $CTNAME ($CTID)..."
if $DRY_RUN; then
log_dry_run "Would execute upgrade on $CTNAME ($CTID) with command: $UPDATE_CMD && $UPGRADE_CMD"
else
if confirm_action "Execute upgrade on $CTNAME ($CTID) with command: $UPDATE_CMD && $UPGRADE_CMD"; then
if pct exec "$CTID" -- /bin/sh -c "$UPDATE_CMD && $UPGRADE_CMD"; then
log_inf "Upgrade completed for $CTNAME ($CTID)"
else
log_err "Upgrade failed for $CTNAME ($CTID)"
continue
fi
else
log_wrn "Upgrade skipped for $CTNAME ($CTID)"
fi
fi
;;
esac
done
# Print summary of statuses
if [ "$ACTION" == "s" ] && [ ${#STATUS_SUMMARIES[@]} -gt 0 ]; then
log_inf "Summary of container statuses:"
for summary in "${STATUS_SUMMARIES[@]}"; do
log_inf "$summary"
done
fi