Update lxc-prehook.sh
This commit is contained in:
@@ -23,6 +23,16 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_NAME="$(basename "$0")"
|
||||||
|
HOOKSCRIPT_FILE="/var/lib/vz/snippets/guest-customize.sh"
|
||||||
|
HOOKSCRIPT_VOLUME_ID="local:snippets/guest-customize.sh"
|
||||||
|
CONFIG_FILE="/etc/default/pve-auto-customize"
|
||||||
|
APPLICATOR_FILE="/usr/local/bin/pve-apply-guest-customize.sh"
|
||||||
|
PATH_UNIT_FILE="/etc/systemd/system/pve-auto-customize.path"
|
||||||
|
SERVICE_UNIT_FILE="/etc/systemd/system/pve-auto-customize.service"
|
||||||
|
README_FILE="/etc/pve-auto-customize/README"
|
||||||
|
EXAMPLE_OVERRIDE_FILE="/etc/pve-auto-customize/100.conf.example"
|
||||||
|
|
||||||
function header_info() {
|
function header_info() {
|
||||||
clear
|
clear
|
||||||
cat <<"EOF"
|
cat <<"EOF"
|
||||||
@@ -84,6 +94,20 @@ require_pve() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print_usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage: ${SCRIPT_NAME} [OPTIONS]
|
||||||
|
|
||||||
|
Install or remove the Proxmox LXC guest customization hook system.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--install Install/update hookscript automation (default)
|
||||||
|
--uninstall Remove automation and cleanup hookscript assignments
|
||||||
|
--status Show current installation state
|
||||||
|
--help, -h Show this help message
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
create_directories() {
|
create_directories() {
|
||||||
msg_info "Creating required directories"
|
msg_info "Creating required directories"
|
||||||
mkdir -p /var/lib/vz/snippets
|
mkdir -p /var/lib/vz/snippets
|
||||||
@@ -95,8 +119,13 @@ create_directories() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
create_main_config() {
|
create_main_config() {
|
||||||
|
if [[ -f "$CONFIG_FILE" ]]; then
|
||||||
|
msg_ok "Main configuration already exists, keeping existing file"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
msg_info "Creating main configuration"
|
msg_info "Creating main configuration"
|
||||||
cat <<'EOF' >/etc/default/pve-auto-customize
|
cat <<'EOF' >"$CONFIG_FILE"
|
||||||
#
|
#
|
||||||
# Configuration for the Proxmox Automatic Guest Customization Hook
|
# Configuration for the Proxmox Automatic Guest Customization Hook
|
||||||
#
|
#
|
||||||
@@ -140,10 +169,10 @@ RETRY_SLEEP=3
|
|||||||
# APT / PACKAGE HANDLING
|
# APT / PACKAGE HANDLING
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
# Whether to run apt-get update before package installation
|
# Whether to run apt update before package installation
|
||||||
APT_UPDATE=1
|
APT_UPDATE=1
|
||||||
|
|
||||||
# apt-get install options
|
# apt install options
|
||||||
APT_INSTALL_OPTS="-y --no-install-recommends"
|
APT_INSTALL_OPTS="-y --no-install-recommends"
|
||||||
|
|
||||||
# Optional cleanup after installs
|
# Optional cleanup after installs
|
||||||
@@ -213,13 +242,18 @@ ENABLE_NALA_ALIASES=0
|
|||||||
# FASTFETCH_LOGO_PATH=""
|
# FASTFETCH_LOGO_PATH=""
|
||||||
#
|
#
|
||||||
EOF
|
EOF
|
||||||
chmod 0644 /etc/default/pve-auto-customize
|
chmod 0644 "$CONFIG_FILE"
|
||||||
msg_ok "Created main configuration"
|
msg_ok "Created main configuration"
|
||||||
}
|
}
|
||||||
|
|
||||||
create_example_override() {
|
create_example_override() {
|
||||||
|
if [[ -f "$EXAMPLE_OVERRIDE_FILE" ]]; then
|
||||||
|
msg_ok "Example per-CT override already exists, keeping existing file"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
msg_info "Creating example per-CT override"
|
msg_info "Creating example per-CT override"
|
||||||
cat <<'EOF' >/etc/pve-auto-customize/100.conf.example
|
cat <<'EOF' >"$EXAMPLE_OVERRIDE_FILE"
|
||||||
#
|
#
|
||||||
# Example per-CT override for CT 100
|
# Example per-CT override for CT 100
|
||||||
# Rename to:
|
# Rename to:
|
||||||
@@ -241,13 +275,13 @@ create_example_override() {
|
|||||||
# Force a local custom profile package definition:
|
# Force a local custom profile package definition:
|
||||||
# PROFILE_shell_PACKAGES="bash-completion plocate eza"
|
# PROFILE_shell_PACKAGES="bash-completion plocate eza"
|
||||||
EOF
|
EOF
|
||||||
chmod 0644 /etc/pve-auto-customize/100.conf.example
|
chmod 0644 "$EXAMPLE_OVERRIDE_FILE"
|
||||||
msg_ok "Created example per-CT override"
|
msg_ok "Created example per-CT override"
|
||||||
}
|
}
|
||||||
|
|
||||||
create_hookscript() {
|
create_hookscript() {
|
||||||
msg_info "Creating guest customization hookscript"
|
msg_info "Creating guest customization hookscript"
|
||||||
cat <<'EOF' >/var/lib/vz/snippets/guest-customize.sh
|
cat <<'EOF' >"$HOOKSCRIPT_FILE"
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -399,34 +433,55 @@ resolve_requested_packages() {
|
|||||||
|
|
||||||
run_apt_update_if_needed() {
|
run_apt_update_if_needed() {
|
||||||
if [[ "$APT_UPDATE" == "1" ]]; then
|
if [[ "$APT_UPDATE" == "1" ]]; then
|
||||||
log "Running apt-get update"
|
log "Running apt update"
|
||||||
retry_pct_exec bash -c "export DEBIAN_FRONTEND=noninteractive; apt-get update"
|
retry_pct_exec bash -c "export DEBIAN_FRONTEND=noninteractive; apt update"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is_valid_package_name() {
|
||||||
|
local pkg="$1"
|
||||||
|
[[ "$pkg" =~ ^[a-z0-9][a-z0-9+.-]*(:[a-z0-9-]+)?$ ]]
|
||||||
|
}
|
||||||
|
|
||||||
install_packages() {
|
install_packages() {
|
||||||
local packages=("$@")
|
local packages=("$@")
|
||||||
|
local safe_packages=()
|
||||||
|
local pkg
|
||||||
|
|
||||||
if (( ${#packages[@]} == 0 )); then
|
if (( ${#packages[@]} == 0 )); then
|
||||||
log "No packages requested"
|
log "No packages requested"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log "Installing packages: ${packages[*]}"
|
for pkg in "${packages[@]}"; do
|
||||||
|
if is_valid_package_name "$pkg"; then
|
||||||
|
safe_packages+=("$pkg")
|
||||||
|
else
|
||||||
|
log "Skipping invalid package token: $pkg"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( ${#safe_packages[@]} == 0 )); then
|
||||||
|
log "No valid packages left after validation"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Installing packages: ${safe_packages[*]}"
|
||||||
retry_pct_exec bash -c "
|
retry_pct_exec bash -c "
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
apt-get install $APT_INSTALL_OPTS ${packages[*]}
|
apt install $APT_INSTALL_OPTS ${safe_packages[*]}
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
|
|
||||||
run_optional_cleanup() {
|
run_optional_cleanup() {
|
||||||
if [[ "$APT_AUTOREMOVE" == "1" ]]; then
|
if [[ "$APT_AUTOREMOVE" == "1" ]]; then
|
||||||
log "Running apt-get autoremove"
|
log "Running apt autoremove"
|
||||||
retry_pct_exec bash -c "export DEBIAN_FRONTEND=noninteractive; apt-get autoremove -y"
|
retry_pct_exec bash -c "export DEBIAN_FRONTEND=noninteractive; apt autoremove -y"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$APT_CLEAN" == "1" ]]; then
|
if [[ "$APT_CLEAN" == "1" ]]; then
|
||||||
log "Running apt-get clean"
|
log "Running apt clean"
|
||||||
retry_pct_exec bash -c "apt-get clean"
|
retry_pct_exec bash -c "apt clean"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -563,13 +618,13 @@ main() {
|
|||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
EOF
|
EOF
|
||||||
chmod +x /var/lib/vz/snippets/guest-customize.sh
|
chmod +x "$HOOKSCRIPT_FILE"
|
||||||
msg_ok "Created guest customization hookscript"
|
msg_ok "Created guest customization hookscript"
|
||||||
}
|
}
|
||||||
|
|
||||||
create_applicator_script() {
|
create_applicator_script() {
|
||||||
msg_info "Creating hook applicator script"
|
msg_info "Creating hook applicator script"
|
||||||
cat <<'EOF' >/usr/local/bin/pve-apply-guest-customize.sh
|
cat <<'EOF' >"$APPLICATOR_FILE"
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -604,6 +659,9 @@ pct list | awk 'NR>1 {print $1}' | while read -r CTID; do
|
|||||||
if [[ "$current_hook" == "$HOOKSCRIPT_VOLUME_ID" ]]; then
|
if [[ "$current_hook" == "$HOOKSCRIPT_VOLUME_ID" ]]; then
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
log "CT $CTID already has another hookscript ($current_hook). Leaving unchanged"
|
||||||
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log "Applying hookscript to CT $CTID"
|
log "Applying hookscript to CT $CTID"
|
||||||
@@ -611,24 +669,25 @@ pct list | awk 'NR>1 {print $1}' | while read -r CTID; do
|
|||||||
log "Failed to apply hookscript to CT $CTID"
|
log "Failed to apply hookscript to CT $CTID"
|
||||||
done
|
done
|
||||||
EOF
|
EOF
|
||||||
chmod +x /usr/local/bin/pve-apply-guest-customize.sh
|
chmod +x "$APPLICATOR_FILE"
|
||||||
msg_ok "Created hook applicator script"
|
msg_ok "Created hook applicator script"
|
||||||
}
|
}
|
||||||
|
|
||||||
create_systemd_units() {
|
create_systemd_units() {
|
||||||
msg_info "Creating systemd units"
|
msg_info "Creating systemd units"
|
||||||
cat <<'EOF' >/etc/systemd/system/pve-auto-customize.path
|
cat <<'EOF' >"$PATH_UNIT_FILE"
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Watch for new Proxmox LXC configs and apply guest customization hook
|
Description=Watch for new Proxmox LXC configs and apply guest customization hook
|
||||||
|
|
||||||
[Path]
|
[Path]
|
||||||
PathModified=/etc/pve/lxc/
|
PathExistsGlob=/etc/pve/lxc/*.conf
|
||||||
|
Unit=pve-auto-customize.service
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
cat <<'EOF' >/etc/systemd/system/pve-auto-customize.service
|
cat <<'EOF' >"$SERVICE_UNIT_FILE"
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Automatically add guest customization hookscript to LXCs
|
Description=Automatically add guest customization hookscript to LXCs
|
||||||
|
|
||||||
@@ -636,12 +695,18 @@ Description=Automatically add guest customization hookscript to LXCs
|
|||||||
Type=oneshot
|
Type=oneshot
|
||||||
ExecStart=/usr/local/bin/pve-apply-guest-customize.sh
|
ExecStart=/usr/local/bin/pve-apply-guest-customize.sh
|
||||||
EOF
|
EOF
|
||||||
|
chmod 0644 "$PATH_UNIT_FILE" "$SERVICE_UNIT_FILE"
|
||||||
msg_ok "Created systemd units"
|
msg_ok "Created systemd units"
|
||||||
}
|
}
|
||||||
|
|
||||||
create_readme() {
|
create_readme() {
|
||||||
|
if [[ -f "$README_FILE" ]]; then
|
||||||
|
msg_ok "Documentation already exists, keeping existing file"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
msg_info "Creating documentation"
|
msg_info "Creating documentation"
|
||||||
cat <<'EOF' >/etc/pve-auto-customize/README
|
cat <<'EOF' >"$README_FILE"
|
||||||
Proxmox Auto Guest Customize
|
Proxmox Auto Guest Customize
|
||||||
============================
|
============================
|
||||||
|
|
||||||
@@ -700,7 +765,7 @@ Logs
|
|||||||
journalctl -fu pve-auto-customize.service
|
journalctl -fu pve-auto-customize.service
|
||||||
journalctl -t pve-auto-customize -n 100
|
journalctl -t pve-auto-customize -n 100
|
||||||
EOF
|
EOF
|
||||||
chmod 0644 /etc/pve-auto-customize/README
|
chmod 0644 "$README_FILE"
|
||||||
msg_ok "Created documentation"
|
msg_ok "Created documentation"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -713,11 +778,73 @@ enable_systemd() {
|
|||||||
|
|
||||||
initial_apply() {
|
initial_apply() {
|
||||||
msg_info "Performing initial apply for existing LXCs"
|
msg_info "Performing initial apply for existing LXCs"
|
||||||
(/usr/local/bin/pve-apply-guest-customize.sh >/dev/null 2>&1) &
|
("$APPLICATOR_FILE" >/dev/null 2>&1) &
|
||||||
spinner
|
spinner
|
||||||
msg_ok "Initial apply complete"
|
msg_ok "Initial apply complete"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
remove_hookscript_assignments() {
|
||||||
|
msg_info "Removing hookscript assignment from LXCs using guest-customize"
|
||||||
|
|
||||||
|
pct list | awk 'NR>1 {print $1}' | while read -r vmid; do
|
||||||
|
current_hook="$(pct config "$vmid" | awk -F': ' '/^hookscript:/ {print $2}')"
|
||||||
|
if [[ "$current_hook" == "$HOOKSCRIPT_VOLUME_ID" ]]; then
|
||||||
|
pct set "$vmid" --delete hookscript >/dev/null 2>&1 && msg_ok "Removed hookscript from LXC $vmid"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
print_status() {
|
||||||
|
local installed="no"
|
||||||
|
local watcher_state="inactive"
|
||||||
|
|
||||||
|
[[ -f "$HOOKSCRIPT_FILE" ]] && installed="yes"
|
||||||
|
if systemctl is-active --quiet pve-auto-customize.path; then
|
||||||
|
watcher_state="active"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "pve-auto-customize status"
|
||||||
|
echo "-------------------------"
|
||||||
|
echo "Hookscript file: $installed"
|
||||||
|
echo "Path watcher state: $watcher_state"
|
||||||
|
echo "Config file: $CONFIG_FILE"
|
||||||
|
echo "Applicator file: $APPLICATOR_FILE"
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
install_stack() {
|
||||||
|
create_directories
|
||||||
|
create_main_config
|
||||||
|
create_example_override
|
||||||
|
create_hookscript
|
||||||
|
create_applicator_script
|
||||||
|
create_systemd_units
|
||||||
|
create_readme
|
||||||
|
enable_systemd
|
||||||
|
initial_apply
|
||||||
|
print_summary
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstall_stack() {
|
||||||
|
remove_hookscript_assignments
|
||||||
|
|
||||||
|
msg_info "Stopping and disabling systemd units"
|
||||||
|
systemctl disable --now pve-auto-customize.path >/dev/null 2>&1 || true
|
||||||
|
systemctl disable --now pve-auto-customize.service >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
msg_info "Removing installed files"
|
||||||
|
rm -f "$HOOKSCRIPT_FILE" "$APPLICATOR_FILE" "$PATH_UNIT_FILE" "$SERVICE_UNIT_FILE"
|
||||||
|
|
||||||
|
if systemctl daemon-reload >/dev/null 2>&1; then
|
||||||
|
msg_ok "systemd daemon reloaded"
|
||||||
|
else
|
||||||
|
msg_error "Failed to reload systemd daemon"
|
||||||
|
fi
|
||||||
|
|
||||||
|
msg_ok "Uninstall complete"
|
||||||
|
}
|
||||||
|
|
||||||
print_summary() {
|
print_summary() {
|
||||||
echo
|
echo
|
||||||
echo -e "${GN}Installation successful!${CL}"
|
echo -e "${GN}Installation successful!${CL}"
|
||||||
@@ -750,31 +877,55 @@ main() {
|
|||||||
require_root
|
require_root
|
||||||
require_pve
|
require_pve
|
||||||
|
|
||||||
echo -e "\nThis script will install an optional LXC guest customization framework on this Proxmox VE host."
|
local mode="install"
|
||||||
echo -e "It will create files in:"
|
case "${1:-}" in
|
||||||
echo -e " - /var/lib/vz/snippets/"
|
"" | --install)
|
||||||
echo -e " - /usr/local/bin/"
|
mode="install"
|
||||||
echo -e " - /etc/default/"
|
;;
|
||||||
echo -e " - /etc/pve-auto-customize/"
|
--uninstall)
|
||||||
echo -e " - /etc/systemd/system/\n"
|
mode="uninstall"
|
||||||
|
;;
|
||||||
|
--status)
|
||||||
|
print_status
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
--help | -h)
|
||||||
|
print_usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
msg_error "Unknown option: $1"
|
||||||
|
print_usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
read -r -p "Do you want to proceed with the installation? (y/n): " reply
|
if [[ "$mode" == "install" ]]; then
|
||||||
|
echo -e "\nThis script will install an optional LXC guest customization framework on this Proxmox VE host."
|
||||||
|
echo -e "It will create/update files in:"
|
||||||
|
echo -e " - /var/lib/vz/snippets/"
|
||||||
|
echo -e " - /usr/local/bin/"
|
||||||
|
echo -e " - /etc/default/"
|
||||||
|
echo -e " - /etc/pve-auto-customize/"
|
||||||
|
echo -e " - /etc/systemd/system/\n"
|
||||||
|
else
|
||||||
|
echo -e "\nThis will uninstall the pve-auto-customize automation and remove managed files."
|
||||||
|
echo -e "Existing CT hookscript assignments pointing to guest-customize will be removed.\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -r -p "Do you want to proceed? (y/n): " reply
|
||||||
if [[ ! "$reply" =~ ^[Yy]$ ]]; then
|
if [[ ! "$reply" =~ ^[Yy]$ ]]; then
|
||||||
msg_error "Installation cancelled"
|
msg_error "Operation cancelled"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo
|
echo
|
||||||
create_directories
|
|
||||||
create_main_config
|
if [[ "$mode" == "install" ]]; then
|
||||||
create_example_override
|
install_stack
|
||||||
create_hookscript
|
else
|
||||||
create_applicator_script
|
uninstall_stack
|
||||||
create_systemd_units
|
fi
|
||||||
create_readme
|
|
||||||
enable_systemd
|
|
||||||
initial_apply
|
|
||||||
print_summary
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|||||||
Reference in New Issue
Block a user