Initial commit: Nextcloud bare/Hansson → AIO migration toolkit
Scripts 01-05 voor sequentiële migratie: transfer, staging, upgrade-keten, AIO-install en DB-import. PostgreSQL + MySQL/MariaDB bronnen ondersteund. Proxmox/OPNSense VM-builder optioneel in proxmox/. README met 12 gotchas. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
# Lokale config met echte IP's, wachtwoorden en domeinen — nooit committen
|
||||||
|
config.local.sh
|
||||||
|
*.local.sh
|
||||||
|
|
||||||
|
# Logs en tijdelijke bestanden
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
Executable
+89
@@ -0,0 +1,89 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# 01-transfer.sh — Rsync bestanden + database-dump van bronserver naar doelserver.
|
||||||
|
#
|
||||||
|
# Wat het doet:
|
||||||
|
# 1. Genereert SSH-key op doel en voegt die toe aan bron (authorized_keys)
|
||||||
|
# 2. Rsynct NC-app, configs, crontabs → /root/staging/
|
||||||
|
# 3. Maakt een database-dump op de bron (PostgreSQL of MySQL/MariaDB)
|
||||||
|
# 4. Rsynct ncdata → TARGET_DATA_PATH (herstart-veilig via --partial)
|
||||||
|
#
|
||||||
|
# Herstart-veilig: rsync --partial; dit script is idempotent (delta op re-run).
|
||||||
|
# Voortgang volgen: ssh <doel> 'tail -f /var/log/pull-source.log'
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(dirname "$0")/config.sh"
|
||||||
|
log() { echo "[$(date '+%H:%M:%S')] $*"; }
|
||||||
|
|
||||||
|
# ── STAP 1: SSH-key op doel → bron ───────────────────────────────────────────
|
||||||
|
log "SSH-key genereren op doel (indien nodig)..."
|
||||||
|
ssh "${TARGET_SSH}" "test -f /root/.ssh/id_ed25519 || \
|
||||||
|
ssh-keygen -t ed25519 -N '' -f /root/.ssh/id_ed25519 -C nc-migration-pull"
|
||||||
|
|
||||||
|
TARGETKEY=$(ssh "${TARGET_SSH}" "cat /root/.ssh/id_ed25519.pub")
|
||||||
|
|
||||||
|
log "Key toevoegen aan bron authorized_keys (indien nodig)..."
|
||||||
|
ssh "${SOURCE_USER}@${SOURCE_HOST}" \
|
||||||
|
"grep -qF '${TARGETKEY}' /root/.ssh/authorized_keys 2>/dev/null || \
|
||||||
|
echo '${TARGETKEY}' >> /root/.ssh/authorized_keys"
|
||||||
|
|
||||||
|
log "Hostkey accepteren + verbinding testen van doel naar bron..."
|
||||||
|
ssh "${TARGET_SSH}" "
|
||||||
|
ssh-keyscan -H ${SOURCE_HOST} >> /root/.ssh/known_hosts 2>/dev/null
|
||||||
|
ssh -o ConnectTimeout=10 ${SOURCE_USER}@${SOURCE_HOST} hostname
|
||||||
|
"
|
||||||
|
|
||||||
|
# ── DB-dump commando samenstellen ────────────────────────────────────────────
|
||||||
|
# AIO gebruikt PostgreSQL; als de bron MySQL/MariaDB is, wordt de DB later
|
||||||
|
# geconverteerd via 'occ db:convert-type pgsql' (na de upgrade-keten, voor AIO-import).
|
||||||
|
if [[ "${SOURCE_DB_TYPE}" == "mysql" ]]; then
|
||||||
|
# Wachtwoord op de command line is niet ideaal maar werkt voor eenmalige migratie.
|
||||||
|
# Alternatief: zet een .my.cnf op de bronserver met [mysqldump] credentials.
|
||||||
|
DUMP_CMD="mysqldump -u '${SOURCE_DB_USER}' -p'${SOURCE_DB_PASS}' '${SOURCE_DB}' | gzip"
|
||||||
|
else
|
||||||
|
DUMP_CMD="sudo -u postgres pg_dump '${SOURCE_DB}' | gzip"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── STAP 2: Pull-script op doel plaatsen en starten ──────────────────────────
|
||||||
|
log "pull-source.sh plaatsen op doel..."
|
||||||
|
ssh "${TARGET_SSH}" "cat > /root/pull-source.sh" << EOF
|
||||||
|
#!/bin/bash
|
||||||
|
# Draait op doel. Log: /var/log/pull-source.log
|
||||||
|
set -uo pipefail
|
||||||
|
SRC="${SOURCE_USER}@${SOURCE_HOST}"
|
||||||
|
{
|
||||||
|
echo "===== START \$(date) ====="
|
||||||
|
mkdir -p /root/staging/etc
|
||||||
|
|
||||||
|
echo "== 1/4 NC-app =="
|
||||||
|
rsync -aHAX --numeric-ids --partial \
|
||||||
|
\${SRC}:${SOURCE_NC_PATH}/ /root/staging/nextcloud-app/
|
||||||
|
|
||||||
|
echo "== 2/4 configs =="
|
||||||
|
rsync -aH --numeric-ids \${SRC}:/etc/apache2/ /root/staging/etc/apache2/ 2>/dev/null || true
|
||||||
|
rsync -aH --numeric-ids \${SRC}:/etc/php/ /root/staging/etc/php/ 2>/dev/null || true
|
||||||
|
rsync -aH --numeric-ids \${SRC}:/etc/letsencrypt/ /root/staging/etc/letsencrypt/ 2>/dev/null || true
|
||||||
|
rsync -aH --numeric-ids \${SRC}:/var/scripts/ /root/staging/var-scripts/ 2>/dev/null || true
|
||||||
|
ssh \${SRC} 'crontab -l' > /root/staging/crontab-root.txt 2>/dev/null || true
|
||||||
|
ssh \${SRC} 'crontab -u www-data -l' > /root/staging/crontab-www-data.txt 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "== 3/4 database dump (${SOURCE_DB_TYPE}: ${SOURCE_DB}) =="
|
||||||
|
ssh \${SRC} '${DUMP_CMD}' > /root/staging/${SOURCE_DB}-\$(date +%F).sql.gz
|
||||||
|
ls -lh /root/staging/${SOURCE_DB}-*.sql.gz | tail -1
|
||||||
|
|
||||||
|
echo "== 4/4 ncdata =="
|
||||||
|
rsync -aHAX --numeric-ids --partial --info=stats1 \
|
||||||
|
\${SRC}:${SOURCE_DATA_PATH}/ ${TARGET_DATA_PATH}/
|
||||||
|
|
||||||
|
echo "===== KLAAR \$(date) ====="
|
||||||
|
df -h ${TARGET_DATA_PATH}
|
||||||
|
} >> /var/log/pull-source.log 2>&1
|
||||||
|
EOF
|
||||||
|
ssh "${TARGET_SSH}" "chmod +x /root/pull-source.sh"
|
||||||
|
|
||||||
|
# ── STAP 3: Starten met nohup ────────────────────────────────────────────────
|
||||||
|
log "Transfer starten (nohup, herstart-veilig)..."
|
||||||
|
ssh "${TARGET_SSH}" "nohup /root/pull-source.sh > /dev/null 2>&1 & echo PID: \$!"
|
||||||
|
|
||||||
|
log "✓ Transfer draait op de achtergrond."
|
||||||
|
log " Voortgang: ssh <doel> 'tail -f /var/log/pull-source.log'"
|
||||||
|
log " Schijfruimte: ssh <doel> 'df -h ${TARGET_DATA_PATH}'"
|
||||||
Executable
+215
@@ -0,0 +1,215 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# 02-build-staging.sh — Bouwt de staging NC-stack op de doelserver.
|
||||||
|
#
|
||||||
|
# Doel: exact genoeg om 'occ upgrade' te kunnen draaien richting AIO-doelversie.
|
||||||
|
# Apache luistert alleen op :80 intern; geen SSL.
|
||||||
|
# Na geslaagde AIO-import: apt purge apache2 php8.3* postgresql mariadb-server
|
||||||
|
#
|
||||||
|
# Ondersteunt PostgreSQL en MySQL/MariaDB als brontype (SOURCE_DB_TYPE in config.sh).
|
||||||
|
# AIO gebruikt altijd PostgreSQL. Bij MySQL-bron: na de upgrade-keten converteren
|
||||||
|
# met 'occ db:convert-type pgsql' vóór 05-import.sh.
|
||||||
|
#
|
||||||
|
# Log op doel: /var/log/build-staging.log
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(dirname "$0")/config.sh"
|
||||||
|
log() { echo "[$(date '+%H:%M:%S')] $*"; }
|
||||||
|
|
||||||
|
log "Staging stack bouwen op ${TARGET_SSH}..."
|
||||||
|
|
||||||
|
ssh "${TARGET_SSH}" bash -s << REMOTE
|
||||||
|
set -euo pipefail
|
||||||
|
log() { echo "[\$(date '+%H:%M:%S')] \$*"; }
|
||||||
|
|
||||||
|
# Config-variabelen (expanded door lokale shell)
|
||||||
|
STAGING_DB="${STAGING_DB}"
|
||||||
|
STAGING_DB_USER="${STAGING_DB_USER}"
|
||||||
|
STAGING_DB_PASS="${STAGING_DB_PASS}"
|
||||||
|
DB_TYPE="${SOURCE_DB_TYPE}"
|
||||||
|
TARGET_DATA="${TARGET_DATA_PATH}"
|
||||||
|
SOURCE_DB_NAME="${SOURCE_DB}"
|
||||||
|
|
||||||
|
{
|
||||||
|
# ── 1. PHP 8.3 repo + pakketten ───────────────────────────────────────────────
|
||||||
|
log "1/7 Pakketten installeren..."
|
||||||
|
# Debian 13 levert PHP 8.4; NC v30 vereist max PHP 8.3 → sury.org repo.
|
||||||
|
# Pas dit aan als je start vanaf NC v32+ (dan werkt PHP 8.4 wel).
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y curl ca-certificates lsb-release gnupg2 2>&1 | tail -1
|
||||||
|
curl -fsSL https://packages.sury.org/php/apt.gpg \
|
||||||
|
-o /etc/apt/keyrings/sury-php.gpg
|
||||||
|
echo "deb [signed-by=/etc/apt/keyrings/sury-php.gpg] https://packages.sury.org/php/ \$(lsb_release -sc) main" \
|
||||||
|
> /etc/apt/sources.list.d/sury-php.list
|
||||||
|
apt-get update -qq
|
||||||
|
|
||||||
|
# PHP-extensies gemeenschappelijk
|
||||||
|
PHP_COMMON="php8.3-fpm php8.3-cli php8.3-gd php8.3-curl php8.3-xml php8.3-zip
|
||||||
|
php8.3-mbstring php8.3-intl php8.3-bcmath php8.3-gmp
|
||||||
|
php8.3-imagick php8.3-redis php8.3-apcu php8.3-ldap php8.3-bz2"
|
||||||
|
|
||||||
|
if [[ "\$DB_TYPE" == "mysql" ]]; then
|
||||||
|
apt-get install -y apache2 libapache2-mod-fcgid \
|
||||||
|
mariadb-server \
|
||||||
|
php8.3-mysql \
|
||||||
|
\$PHP_COMMON redis-server 2>&1 | tail -3
|
||||||
|
else
|
||||||
|
apt-get install -y apache2 libapache2-mod-fcgid \
|
||||||
|
postgresql \
|
||||||
|
php8.3-pgsql \
|
||||||
|
\$PHP_COMMON redis-server 2>&1 | tail -3
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 2. NC-app op zijn plek ────────────────────────────────────────────────────
|
||||||
|
log "2/7 NC-app kopiëren naar /var/www/nextcloud..."
|
||||||
|
rsync -a --delete /root/staging/nextcloud-app/ /var/www/nextcloud/
|
||||||
|
chown -R www-data:www-data /var/www/nextcloud
|
||||||
|
chmod -R 750 /var/www/nextcloud
|
||||||
|
chown -R www-data:www-data "\$TARGET_DATA"
|
||||||
|
|
||||||
|
# ── 3. Database: aanmaken + dump restoren ────────────────────────────────────
|
||||||
|
log "3/7 Database inrichten (\$DB_TYPE)..."
|
||||||
|
|
||||||
|
DUMP_FILE=\$(ls -t /root/staging/\${SOURCE_DB_NAME}-*.sql.gz 2>/dev/null | head -1)
|
||||||
|
[ -z "\$DUMP_FILE" ] && { echo "FOUT: geen dump gevonden in /root/staging/"; exit 1; }
|
||||||
|
log "Dump: \$DUMP_FILE (\$(du -sh "\$DUMP_FILE" | cut -f1))"
|
||||||
|
|
||||||
|
if [[ "\$DB_TYPE" == "mysql" ]]; then
|
||||||
|
systemctl enable --now mariadb
|
||||||
|
mysql -e "CREATE USER IF NOT EXISTS '\${STAGING_DB_USER}'@'localhost' IDENTIFIED BY '\${STAGING_DB_PASS}';" 2>/dev/null || true
|
||||||
|
mysql -e "CREATE DATABASE IF NOT EXISTS \${STAGING_DB} CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" 2>/dev/null || true
|
||||||
|
mysql -e "GRANT ALL PRIVILEGES ON \${STAGING_DB}.* TO '\${STAGING_DB_USER}'@'localhost'; FLUSH PRIVILEGES;"
|
||||||
|
log "Dump restoren naar MariaDB..."
|
||||||
|
zcat "\$DUMP_FILE" | mysql -u "\$STAGING_DB_USER" -p"\$STAGING_DB_PASS" "\$STAGING_DB"
|
||||||
|
else
|
||||||
|
systemctl enable --now postgresql
|
||||||
|
sudo -u postgres psql -tc "SELECT 1 FROM pg_roles WHERE rolname='\$STAGING_DB_USER'" | grep -q 1 || \
|
||||||
|
sudo -u postgres psql -c "CREATE USER \${STAGING_DB_USER} WITH PASSWORD '\${STAGING_DB_PASS}';"
|
||||||
|
sudo -u postgres psql -tc "SELECT 1 FROM pg_database WHERE datname='\$STAGING_DB'" | grep -q 1 || \
|
||||||
|
sudo -u postgres createdb -O "\$STAGING_DB_USER" "\$STAGING_DB"
|
||||||
|
log "Dump restoren naar PostgreSQL..."
|
||||||
|
# Restore als de DB-user zodat tabeleigenaarschap direct klopt
|
||||||
|
PGPASSWORD="\$STAGING_DB_PASS" zcat "\$DUMP_FILE" | \
|
||||||
|
sudo -u postgres psql -U "\$STAGING_DB_USER" "\$STAGING_DB"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 4. PHP-FPM pool ──────────────────────────────────────────────────────────
|
||||||
|
log "4/7 PHP-FPM pool configureren..."
|
||||||
|
cat > /etc/php/8.3/fpm/pool.d/nextcloud.conf << 'EOF'
|
||||||
|
[nextcloud]
|
||||||
|
user = www-data
|
||||||
|
group = www-data
|
||||||
|
listen = /run/php/php8.3-fpm.nextcloud.sock
|
||||||
|
listen.owner = www-data
|
||||||
|
listen.group = www-data
|
||||||
|
listen.mode = 0660
|
||||||
|
pm = dynamic
|
||||||
|
pm.max_children = 20
|
||||||
|
pm.start_servers = 3
|
||||||
|
pm.min_spare_servers = 2
|
||||||
|
pm.max_spare_servers = 10
|
||||||
|
php_admin_value[memory_limit] = 512M
|
||||||
|
php_admin_value[upload_max_filesize] = 10G
|
||||||
|
php_admin_value[post_max_size] = 10G
|
||||||
|
php_admin_value[max_execution_time] = 3600
|
||||||
|
EOF
|
||||||
|
rm -f /etc/php/8.3/fpm/pool.d/www.conf
|
||||||
|
sed -i 's/^memory_limit.*/memory_limit = 512M/' /etc/php/8.3/cli/php.ini
|
||||||
|
# apc.enable_cli=1 is vereist voor occ; zonder dit falen APCu-afhankelijke commando's stil
|
||||||
|
grep -q "apc.enable_cli" /etc/php/8.3/cli/php.ini || echo "apc.enable_cli=1" >> /etc/php/8.3/cli/php.ini
|
||||||
|
systemctl enable --now php8.3-fpm
|
||||||
|
|
||||||
|
# ── 5. Apache vhost (HTTP-only, intern) ──────────────────────────────────────
|
||||||
|
log "5/7 Apache vhost configureren..."
|
||||||
|
a2enmod proxy_fcgi setenvif rewrite headers env dir mime authz_core 2>/dev/null || true
|
||||||
|
a2enconf php8.3-fpm 2>/dev/null || true
|
||||||
|
cat > /etc/apache2/sites-available/nextcloud.conf << 'EOF'
|
||||||
|
<VirtualHost *:80>
|
||||||
|
ServerName _default_
|
||||||
|
DocumentRoot /var/www/nextcloud
|
||||||
|
|
||||||
|
<Directory /var/www/nextcloud>
|
||||||
|
Options +FollowSymlinks
|
||||||
|
AllowOverride All
|
||||||
|
Require all granted
|
||||||
|
<IfModule mod_dav.c>
|
||||||
|
Dav off
|
||||||
|
</IfModule>
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
<FilesMatch \.php$>
|
||||||
|
SetHandler "proxy:unix:/run/php/php8.3-fpm.nextcloud.sock|fcgi://localhost"
|
||||||
|
</FilesMatch>
|
||||||
|
|
||||||
|
ErrorLog \${APACHE_LOG_DIR}/nextcloud-error.log
|
||||||
|
CustomLog \${APACHE_LOG_DIR}/nextcloud-access.log combined
|
||||||
|
</VirtualHost>
|
||||||
|
EOF
|
||||||
|
a2dissite 000-default 2>/dev/null || true
|
||||||
|
a2ensite nextcloud
|
||||||
|
systemctl enable --now apache2
|
||||||
|
apache2ctl configtest
|
||||||
|
|
||||||
|
# ── 6. config.php patchen voor staging ───────────────────────────────────────
|
||||||
|
log "6/7 config.php patchen voor staging (geen SSL, intern)..."
|
||||||
|
# config.php definieert \$CONFIG maar returnt het niet; gebruik require (niet include).
|
||||||
|
if [[ "\$DB_TYPE" == "mysql" ]]; then
|
||||||
|
DB_HOST="localhost"
|
||||||
|
DB_PORT="3306"
|
||||||
|
else
|
||||||
|
DB_HOST="localhost"
|
||||||
|
DB_PORT="5432"
|
||||||
|
fi
|
||||||
|
TARGET_IP=\$(hostname -I | awk '{print \$1}')
|
||||||
|
|
||||||
|
php8.3 -r "
|
||||||
|
require '/var/www/nextcloud/config/config.php';
|
||||||
|
\\\$CONFIG['trusted_domains'] = ['\$TARGET_IP', 'localhost'];
|
||||||
|
\\\$CONFIG['overwrite.cli.url'] = 'http://\$TARGET_IP';
|
||||||
|
\\\$CONFIG['overwriteprotocol'] = 'http';
|
||||||
|
\\\$CONFIG['maintenance'] = false;
|
||||||
|
\\\$CONFIG['dbhost'] = '\$DB_HOST';
|
||||||
|
\\\$CONFIG['dbport'] = '\$DB_PORT';
|
||||||
|
\\\$CONFIG['dbpassword'] = '\$STAGING_DB_PASS';
|
||||||
|
\\\$CONFIG['datadirectory'] = '\$TARGET_DATA';
|
||||||
|
\\\$CONFIG['redis'] = ['host' => '127.0.0.1', 'port' => 6379];
|
||||||
|
\\\$CONFIG['memcache.local'] = '\\\\OC\\\\Memcache\\\\APCu';
|
||||||
|
\\\$CONFIG['memcache.locking'] = '\\\\OC\\\\Memcache\\\\Redis';
|
||||||
|
\\\$out = \"<?php\n\\\\\\\$CONFIG = \" . var_export(\\\$CONFIG, true) . \";\n\";
|
||||||
|
file_put_contents('/var/www/nextcloud/config/config.php', \\\$out);
|
||||||
|
echo 'config.php geschreven' . PHP_EOL;
|
||||||
|
"
|
||||||
|
chown www-data:www-data /var/www/nextcloud/config/config.php
|
||||||
|
|
||||||
|
# ── 7. Cron + verificatie ─────────────────────────────────────────────────────
|
||||||
|
log "7/7 Cron instellen + occ status controleren..."
|
||||||
|
cat > /etc/systemd/system/nextcloud-cron.service << 'EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=Nextcloud cron
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
User=www-data
|
||||||
|
ExecStart=/usr/bin/php -f /var/www/nextcloud/cron.php
|
||||||
|
EOF
|
||||||
|
cat > /etc/systemd/system/nextcloud-cron.timer << 'EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=Nextcloud cron timer
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnBootSec=5min
|
||||||
|
OnUnitActiveSec=5min
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
|
EOF
|
||||||
|
systemctl enable --now nextcloud-cron.timer
|
||||||
|
|
||||||
|
sudo -u www-data php8.3 /var/www/nextcloud/occ status
|
||||||
|
|
||||||
|
log "✓ Staging stack klaar."
|
||||||
|
log " occ: sudo -u www-data php8.3 /var/www/nextcloud/occ <commando>"
|
||||||
|
log " Logs: /var/log/apache2/nextcloud-error.log"
|
||||||
|
|
||||||
|
} 2>&1 | tee /var/log/build-staging.log
|
||||||
|
REMOTE
|
||||||
|
|
||||||
|
log "✓ Script klaar. Voortgang: ssh <doel> 'tail -f /var/log/build-staging.log'"
|
||||||
Executable
+183
@@ -0,0 +1,183 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# 03-upgrade.sh — NC upgrade-keten op de doelserver.
|
||||||
|
#
|
||||||
|
# Draait sequentieel door NC_UPGRADE_VERSIONS uit config.sh.
|
||||||
|
# NC vereist één major per stap: v30 → v31 → v32 → v33.
|
||||||
|
# Na elke major: optioneel een Proxmox-snapshot als terugkeerput.
|
||||||
|
#
|
||||||
|
# Bij MySQL-bron: voeg NA de laatste upgrade-stap het commando toe:
|
||||||
|
# occ db:convert-type --all-apps pgsql <user> <pass> <db>
|
||||||
|
# zodat de DB PostgreSQL is voor AIO-import (05-import.sh).
|
||||||
|
#
|
||||||
|
# Terugzetten naar snapshot: ssh <pve> 'qm rollback <vmid> <naam>' (VM offline)
|
||||||
|
# Log: elke stap print naar stdout; draai met 'tee upgrade.log' om op te slaan.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(dirname "$0")/config.sh"
|
||||||
|
log() { echo "[$(date '+%H:%M:%S')] $*"; }
|
||||||
|
|
||||||
|
OCC="sudo -u www-data php8.3 /var/www/nextcloud/occ"
|
||||||
|
|
||||||
|
# ── Apps uitschakelen die niet draaien op staging ────────────────────────────
|
||||||
|
# AIO vervangt deze later; staging heeft geen Docker/HPB.
|
||||||
|
log "Apps uitschakelen die afhankelijk zijn van Docker/HPB..."
|
||||||
|
ssh "${TARGET_SSH}" "
|
||||||
|
for app in fulltextsearch fulltextsearch_elasticsearch recognize spreed \
|
||||||
|
whiteboard talk_matterbridge notify_push app_api; do
|
||||||
|
${OCC} app:disable \"\$app\" 2>/dev/null && echo \" disabled: \$app\" || true
|
||||||
|
done
|
||||||
|
"
|
||||||
|
|
||||||
|
# ── Upgrade-functie ───────────────────────────────────────────────────────────
|
||||||
|
upgrade_to() {
|
||||||
|
local VERSION="$1"
|
||||||
|
local MAJOR="${VERSION%%.*}"
|
||||||
|
local ARCHIVE="nextcloud-${VERSION}.tar.bz2"
|
||||||
|
local URL="https://download.nextcloud.com/server/releases/${ARCHIVE}"
|
||||||
|
local SNAPSHOT="upgrade-v${MAJOR}-done"
|
||||||
|
|
||||||
|
log "══ NC ${VERSION} ══════════════════════════════════════"
|
||||||
|
|
||||||
|
ssh "${TARGET_SSH}" "${OCC} maintenance:mode --on"
|
||||||
|
|
||||||
|
# Download + bestanden vervangen op de doelserver
|
||||||
|
ssh "${TARGET_SSH}" bash -s << VMEOF
|
||||||
|
set -euo pipefail
|
||||||
|
log() { echo "[\$(date '+%H:%M:%S')] \$*"; }
|
||||||
|
|
||||||
|
cd /tmp
|
||||||
|
if [ ! -f "${ARCHIVE}" ]; then
|
||||||
|
log "Downloaden ${ARCHIVE}..."
|
||||||
|
wget -q --show-progress -O "${ARCHIVE}" "${URL}" 2>&1 | tail -3
|
||||||
|
else
|
||||||
|
log "Archief al aanwezig, overgeslagen."
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Bestanden vervangen..."
|
||||||
|
rm -rf /tmp/nc-new && mkdir /tmp/nc-new
|
||||||
|
tar -xjf "${ARCHIVE}" -C /tmp/nc-new
|
||||||
|
|
||||||
|
cp -a /var/www/nextcloud/config /tmp/nc-config-bak
|
||||||
|
|
||||||
|
# --exclude='/config/' geankerd op root van de bron: raakt geen config/-mappen in apps.
|
||||||
|
# Zonder leading slash zou rsync ook app/*/config/ verwijderen (bug).
|
||||||
|
rsync -a --delete \
|
||||||
|
--exclude='/config/' \
|
||||||
|
--exclude='/data' \
|
||||||
|
/tmp/nc-new/nextcloud/ /var/www/nextcloud/ || true
|
||||||
|
|
||||||
|
# config/ herstellen als rsync die geraakt heeft
|
||||||
|
[ -f /var/www/nextcloud/config/config.php ] || \
|
||||||
|
cp -a /tmp/nc-config-bak/. /var/www/nextcloud/config/
|
||||||
|
|
||||||
|
# Apps zonder appinfo/info.xml crashen occ upgrade — verwijder ze
|
||||||
|
for dir in /var/www/nextcloud/apps/*/; do
|
||||||
|
app=\$(basename "\$dir")
|
||||||
|
[ -f "\$dir/appinfo/info.xml" ] || { rm -rf "\$dir"; echo " opgeruimd (geen info.xml): \$app"; }
|
||||||
|
done
|
||||||
|
|
||||||
|
chown -R www-data:www-data /var/www/nextcloud
|
||||||
|
rm -rf /tmp/nc-new /tmp/nc-config-bak
|
||||||
|
log "Bestanden klaar."
|
||||||
|
VMEOF
|
||||||
|
|
||||||
|
log "occ upgrade..."
|
||||||
|
ssh "${TARGET_SSH}" "${OCC} upgrade 2>&1"
|
||||||
|
|
||||||
|
log "App-updates..."
|
||||||
|
ssh "${TARGET_SSH}" "${OCC} app:update --all 2>&1 || true"
|
||||||
|
|
||||||
|
ssh "${TARGET_SSH}" "${OCC} status"
|
||||||
|
ssh "${TARGET_SSH}" "${OCC} maintenance:mode --off"
|
||||||
|
|
||||||
|
if [[ "${USE_PROXMOX}" == "true" ]]; then
|
||||||
|
log "Snapshot '${SNAPSHOT}' aanmaken..."
|
||||||
|
ssh "${PROXMOX_HOST}" \
|
||||||
|
"qm snapshot ${PROXMOX_VMID} '${SNAPSHOT}' \
|
||||||
|
--description 'NC ${VERSION} upgrade klaar' 2>&1"
|
||||||
|
log "✓ Snapshot klaar."
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "✓ NC ${VERSION} klaar."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Upgrade-keten ─────────────────────────────────────────────────────────────
|
||||||
|
log "Start versie:"
|
||||||
|
ssh "${TARGET_SSH}" "${OCC} status | grep versionstring"
|
||||||
|
|
||||||
|
# App-inventarisatie vóór de keten
|
||||||
|
log "App-inventarisatie opslaan (voor vergelijking achteraf)..."
|
||||||
|
ssh "${TARGET_SSH}" "
|
||||||
|
${OCC} app:list --output=json 2>/dev/null | python3 -c \"
|
||||||
|
import json, sys
|
||||||
|
d = json.load(sys.stdin)
|
||||||
|
for app in sorted(d.get('enabled', {}).keys()):
|
||||||
|
print(app)
|
||||||
|
\" > /tmp/nc-apps-before.txt
|
||||||
|
echo \" \$(wc -l < /tmp/nc-apps-before.txt) apps enabled voor upgrade\"
|
||||||
|
"
|
||||||
|
|
||||||
|
for VERSION in "${NC_UPGRADE_VERSIONS[@]}"; do
|
||||||
|
upgrade_to "${VERSION}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── App-rapport na de keten ───────────────────────────────────────────────────
|
||||||
|
log "══════════════════════════════════════════"
|
||||||
|
log "Upgrade-keten klaar. Eindversie:"
|
||||||
|
ssh "${TARGET_SSH}" "${OCC} status"
|
||||||
|
log ""
|
||||||
|
log "App-rapport (vergelijking voor/na upgrade-keten):"
|
||||||
|
|
||||||
|
ssh "${TARGET_SSH}" "
|
||||||
|
${OCC} app:list --output=json 2>/dev/null | python3 -c \"
|
||||||
|
import json, sys
|
||||||
|
d = json.load(sys.stdin)
|
||||||
|
for app in sorted(d.get('enabled', {}).keys()):
|
||||||
|
print(app)
|
||||||
|
\" > /tmp/nc-apps-after.txt
|
||||||
|
|
||||||
|
# Apps die verdwenen of uitgeschakeld zijn na de keten
|
||||||
|
LOST=\$(comm -23 \
|
||||||
|
<(sort /tmp/nc-apps-before.txt) \
|
||||||
|
<(sort /tmp/nc-apps-after.txt))
|
||||||
|
|
||||||
|
# Apps die door AIO vervangen worden (geen actie nodig)
|
||||||
|
AIO_REPLACES='fulltextsearch fulltextsearch_elasticsearch notify_push
|
||||||
|
spreed whiteboard recognize files_antivirus'
|
||||||
|
|
||||||
|
echo ''
|
||||||
|
echo '── Nog steeds enabled ────────────────────────────'
|
||||||
|
comm -12 <(sort /tmp/nc-apps-before.txt) <(sort /tmp/nc-apps-after.txt)
|
||||||
|
|
||||||
|
echo ''
|
||||||
|
echo '── Uitgeschakeld/verdwenen na upgrade ────────────'
|
||||||
|
if [ -z \"\$LOST\" ]; then
|
||||||
|
echo ' (geen)'
|
||||||
|
else
|
||||||
|
for app in \$LOST; do
|
||||||
|
if echo \"\$AIO_REPLACES\" | grep -qw \"\$app\"; then
|
||||||
|
echo \" [AIO-native] \$app ← vervangen door AIO-container\"
|
||||||
|
else
|
||||||
|
echo \" [ACTIE NODIG] \$app ← handmatig herinstalleren na AIO-import\"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ''
|
||||||
|
echo '── Nieuw disabled in NC app store (incompatibel) ─'
|
||||||
|
${OCC} app:list --output=json 2>/dev/null | python3 -c \"
|
||||||
|
import json, sys
|
||||||
|
d = json.load(sys.stdin)
|
||||||
|
for app in sorted(d.get('disabled', {}).keys()):
|
||||||
|
print(' ' + app)
|
||||||
|
\"
|
||||||
|
"
|
||||||
|
|
||||||
|
if [[ "${SOURCE_DB_TYPE}" == "mysql" ]]; then
|
||||||
|
log "VOLGENDE STAP (MySQL-bron): converteer de DB naar PostgreSQL vóór 05-import.sh:"
|
||||||
|
log " ssh <doel> 'sudo -u www-data php8.3 /var/www/nextcloud/occ \\"
|
||||||
|
log " db:convert-type --all-apps pgsql ${STAGING_DB_USER} ${STAGING_DB_PASS} ${STAGING_DB}'"
|
||||||
|
log " Daarna een verse pg_dump maken en opslaan als /tmp/nc_v33_aio.sql"
|
||||||
|
else
|
||||||
|
log "VOLGENDE STAP: 04-install-aio.sh production"
|
||||||
|
fi
|
||||||
Executable
+145
@@ -0,0 +1,145 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# 04-install-aio.sh — AIO mastercontainer installeren op de doelserver.
|
||||||
|
#
|
||||||
|
# Gebruik:
|
||||||
|
# ./04-install-aio.sh test → NC_STAGING_DOMAIN, poort 11000, skip LE
|
||||||
|
# ./04-install-aio.sh production → NC_DOMAIN, poort 443, Let's Encrypt cert
|
||||||
|
#
|
||||||
|
# Na dit script: handmatige stappen in de AIO-wizard (zie output).
|
||||||
|
# Wacht tot alle containers groen zijn → dan 05-import.sh draaien.
|
||||||
|
#
|
||||||
|
# BELANGRIJK: de mastercontainer bindt poort 443/80/3478 NIET zelf.
|
||||||
|
# AIO's apache-subcontainer beheert die poorten. Als je die poorten wél aan de
|
||||||
|
# mastercontainer geeft, faalt de domeincheck omdat beide containers op 443 luisteren.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(dirname "$0")/config.sh"
|
||||||
|
MODE="${1:-test}"
|
||||||
|
log() { echo "[$(date '+%H:%M:%S')] $*"; }
|
||||||
|
|
||||||
|
if [[ "$MODE" == "production" ]]; then
|
||||||
|
DOMAIN="${NC_DOMAIN}"
|
||||||
|
APACHE_PORT=443
|
||||||
|
SKIP_VALIDATION=""
|
||||||
|
else
|
||||||
|
DOMAIN="${NC_STAGING_DOMAIN}"
|
||||||
|
APACHE_PORT=11000
|
||||||
|
SKIP_VALIDATION="-e SKIP_DOMAIN_VALIDATION=true"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Modus: ${MODE} — domein: ${DOMAIN} — apache-poort: ${APACHE_PORT}"
|
||||||
|
|
||||||
|
# ── Snapshot vóór AIO ────────────────────────────────────────────────────────
|
||||||
|
if [[ "${USE_PROXMOX}" == "true" ]]; then
|
||||||
|
SNAPSHOT="pre-aio-import-${MODE}"
|
||||||
|
log "Snapshot '${SNAPSHOT}' aanmaken..."
|
||||||
|
ssh "${PROXMOX_HOST}" \
|
||||||
|
"qm snapshot ${PROXMOX_VMID} '${SNAPSHOT}' \
|
||||||
|
--description 'Voor AIO start (${MODE})' 2>&1"
|
||||||
|
log "✓ Snapshot klaar."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Staging NC stoppen ───────────────────────────────────────────────────────
|
||||||
|
log "Staging stack stoppen (indien actief)..."
|
||||||
|
ssh "${TARGET_SSH}" "
|
||||||
|
sudo -u www-data php8.3 /var/www/nextcloud/occ maintenance:mode --on 2>/dev/null || true
|
||||||
|
systemctl stop apache2 php8.3-fpm nextcloud-cron.timer 2>/dev/null || true
|
||||||
|
echo 'Staging gestopt.'
|
||||||
|
"
|
||||||
|
|
||||||
|
# ── Docker installeren ───────────────────────────────────────────────────────
|
||||||
|
log "Docker controleren / installeren..."
|
||||||
|
ssh "${TARGET_SSH}" bash -s << 'REMOTE'
|
||||||
|
if command -v docker &>/dev/null; then
|
||||||
|
echo "Docker al aanwezig: $(docker --version)"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y ca-certificates curl gnupg lsb-release 2>&1 | tail -1
|
||||||
|
install -m 0755 -d /etc/apt/keyrings
|
||||||
|
curl -fsSL https://download.docker.com/linux/debian/gpg \
|
||||||
|
-o /etc/apt/keyrings/docker.asc
|
||||||
|
chmod a+r /etc/apt/keyrings/docker.asc
|
||||||
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
|
||||||
|
https://download.docker.com/linux/debian \
|
||||||
|
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
|
||||||
|
> /etc/apt/sources.list.d/docker.list
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin 2>&1 | tail -3
|
||||||
|
systemctl enable --now docker
|
||||||
|
echo "Docker geïnstalleerd: $(docker --version)"
|
||||||
|
REMOTE
|
||||||
|
|
||||||
|
# ── AIO mastercontainer starten ──────────────────────────────────────────────
|
||||||
|
log "AIO mastercontainer starten..."
|
||||||
|
ssh "${TARGET_SSH}" bash -s << REMOTE
|
||||||
|
set -e
|
||||||
|
docker rm -f nextcloud-aio-mastercontainer 2>/dev/null || true
|
||||||
|
|
||||||
|
docker run -d \
|
||||||
|
--name nextcloud-aio-mastercontainer \
|
||||||
|
--restart always \
|
||||||
|
-p 8080:8080 \
|
||||||
|
-e APACHE_PORT=${APACHE_PORT} \
|
||||||
|
-e APACHE_IP_BINDING=0.0.0.0 \
|
||||||
|
-e NEXTCLOUD_DATADIR=${TARGET_DATA_PATH} \
|
||||||
|
${SKIP_VALIDATION} \
|
||||||
|
-v /var/run/docker.sock:/var/run/docker.sock:ro \
|
||||||
|
-v nextcloud_aio_mastercontainer:/mnt/docker-aio-config \
|
||||||
|
nextcloud/all-in-one:latest
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Wachten op AIO start (~15s)..."
|
||||||
|
sleep 15
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "══ AIO PASSPHRASE ══════════════════════════════"
|
||||||
|
docker logs nextcloud-aio-mastercontainer 2>&1 \
|
||||||
|
| grep -i "passphrase\|initial passphrase" | tail -3 \
|
||||||
|
|| docker logs nextcloud-aio-mastercontainer 2>&1 | tail -15
|
||||||
|
echo "════════════════════════════════════════════════"
|
||||||
|
REMOTE
|
||||||
|
|
||||||
|
# ── App-rapport uit stap 03 printen ─────────────────────────────────────────
|
||||||
|
log ""
|
||||||
|
log "Apps die na AIO-import handmatig herinstalleerd moeten worden:"
|
||||||
|
ssh "${TARGET_SSH}" "
|
||||||
|
if [ -f /tmp/nc-apps-before.txt ] && [ -f /tmp/nc-apps-after.txt ]; then
|
||||||
|
AIO_REPLACES='fulltextsearch fulltextsearch_elasticsearch notify_push
|
||||||
|
spreed whiteboard recognize files_antivirus'
|
||||||
|
comm -23 \
|
||||||
|
<(sort /tmp/nc-apps-before.txt) \
|
||||||
|
<(sort /tmp/nc-apps-after.txt) | while read app; do
|
||||||
|
echo \"\$AIO_REPLACES\" | grep -qw \"\$app\" || echo \" occ app:install \$app\"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo ' (voer eerst 03-upgrade.sh uit voor een volledige lijst)'
|
||||||
|
fi
|
||||||
|
" 2>/dev/null || true
|
||||||
|
|
||||||
|
# ── Instructies ──────────────────────────────────────────────────────────────
|
||||||
|
TARGET_IP=$(ssh "${TARGET_SSH}" "hostname -I | awk '{print \$1}'" 2>/dev/null || echo "<doel-ip>")
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
log "══════════════════════════════════════════════════════════════"
|
||||||
|
log "AIO mastercontainer draait. Handmatige stappen in de browser:"
|
||||||
|
log ""
|
||||||
|
log " 1. Open: https://${TARGET_IP}:8080"
|
||||||
|
log " (self-signed cert → klik door de waarschuwing)"
|
||||||
|
log " 2. Voer de passphrase in (zie output hierboven)"
|
||||||
|
log " 3. Stel domein in: ${DOMAIN}"
|
||||||
|
if [[ "$MODE" == "test" ]]; then
|
||||||
|
log " 4. 'Skip domain validation' is al geconfigureerd"
|
||||||
|
fi
|
||||||
|
log " 5. Selecteer optionele containers:"
|
||||||
|
log " Minimaal (test): Imaginary"
|
||||||
|
log " Volledig (prod): Talk, Imaginary, ClamAV, Fulltextsearch, Whiteboard"
|
||||||
|
log " Office-suite: kies één — Collabora, OnlyOffice, Euro-Office, of geen"
|
||||||
|
log " 6. Klik 'Save and start containers'"
|
||||||
|
log " 7. Wacht tot ALLE containers groen zijn (~5-15 min)"
|
||||||
|
log " Let op: eerste start na import geeft zware CPU-piek door"
|
||||||
|
log " Elasticsearch-indexering + ClamAV DB-update + Recognize."
|
||||||
|
log " Plan dit buiten kantooruren."
|
||||||
|
log ""
|
||||||
|
log " Daarna: ./05-import.sh"
|
||||||
|
log "══════════════════════════════════════════════════════════════"
|
||||||
Executable
+154
@@ -0,0 +1,154 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# 05-import.sh — Bestaande NC-database + data importeren in AIO.
|
||||||
|
#
|
||||||
|
# Vereiste: AIO-wizard afgerond, alle containers groen (via https://<doel>:8080).
|
||||||
|
# Draait op de doelserver zelf (gesourcet via SSH).
|
||||||
|
#
|
||||||
|
# Wat het doet:
|
||||||
|
# 1. Dump van de staging-postgres (NC-versie gelijk aan AIO-doelversie)
|
||||||
|
# 2. NC + notify-push stoppen (DB-verbindingen verbreken)
|
||||||
|
# 3. AIO-database vervangen door de staging-dump
|
||||||
|
# 4. NC starten + instanceid/passwordsalt/secret overnemen van staging
|
||||||
|
# 5. maintenance:repair + maintenance:mode --off
|
||||||
|
# 6. groupfolders herinstalleren (AIO start kaal)
|
||||||
|
#
|
||||||
|
# AIO v13+ containernamen (wijken af van oudere docs):
|
||||||
|
# DB-container : nextcloud-aio-database
|
||||||
|
# Database : nextcloud_database
|
||||||
|
# NC-user : oc_nextcloud
|
||||||
|
# Superuser : nextcloud
|
||||||
|
#
|
||||||
|
# Log: /var/log/aio-import.log op de doelserver
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(dirname "$0")/config.sh"
|
||||||
|
log() { echo "[$(date '+%H:%M:%S')] $*"; }
|
||||||
|
|
||||||
|
log "05-import.sh starten op ${TARGET_SSH}..."
|
||||||
|
|
||||||
|
ssh "${TARGET_SSH}" bash -s << REMOTE
|
||||||
|
set -euo pipefail
|
||||||
|
log() { echo "[\$(date '+%H:%M:%S')] \$*"; }
|
||||||
|
|
||||||
|
STAGING_DB="${STAGING_DB}"
|
||||||
|
STAGING_DB_USER="${STAGING_DB_USER}"
|
||||||
|
STAGING_DB_PASS="${STAGING_DB_PASS}"
|
||||||
|
TARGET_DATA="${TARGET_DATA_PATH}"
|
||||||
|
|
||||||
|
NC_CONTAINER=nextcloud-aio-nextcloud
|
||||||
|
DB_CONTAINER=nextcloud-aio-database
|
||||||
|
AIO_DB=nextcloud_database
|
||||||
|
AIO_SUPER=nextcloud
|
||||||
|
AIO_NCUSER=oc_nextcloud
|
||||||
|
DUMP=/tmp/nc_aio_import.sql
|
||||||
|
|
||||||
|
{
|
||||||
|
log "═══ AIO import start ═══"
|
||||||
|
|
||||||
|
# ── Sanity checks ────────────────────────────────────────────────────────────
|
||||||
|
for c in "\$NC_CONTAINER" "\$DB_CONTAINER"; do
|
||||||
|
docker ps --format '{{.Names}}' | grep -q "^\${c}$" || {
|
||||||
|
log "FOUT: container \${c} niet actief."
|
||||||
|
log "Zorg dat alle AIO-containers groen zijn in de wizard (https://<doel>:8080)."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── Staging-waarden uitlezen ─────────────────────────────────────────────────
|
||||||
|
# Deze drie waarden koppelen de NC-sessies aan de database.
|
||||||
|
# Ze MOETEN overeenkomen met de waarden in de te importeren DB.
|
||||||
|
OCC_STAGING="sudo -u www-data php8.3 /var/www/nextcloud/occ"
|
||||||
|
if command -v php8.3 &>/dev/null && [ -f /var/www/nextcloud/occ ]; then
|
||||||
|
INSTANCEID=\$(\$OCC_STAGING config:system:get instanceid 2>/dev/null)
|
||||||
|
PASSWORDSALT=\$(\$OCC_STAGING config:system:get passwordsalt 2>/dev/null)
|
||||||
|
SECRET=\$(\$OCC_STAGING config:system:get secret 2>/dev/null)
|
||||||
|
log "instanceid/passwordsalt/secret uitgelezen uit staging-stack."
|
||||||
|
else
|
||||||
|
log "FOUT: staging-stack (php8.3 / occ) niet beschikbaar."
|
||||||
|
log "Zet INSTANCEID, PASSWORDSALT en SECRET handmatig bovenaan dit script,"
|
||||||
|
log "of herstart 02-build-staging.sh zodat de staging-stack aanwezig is."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# AIO DB-wachtwoord voor oc_nextcloud (staat in AIO's config.php)
|
||||||
|
NCPASS=\$(docker exec "\$NC_CONTAINER" \
|
||||||
|
php /var/www/html/occ config:system:get dbpassword 2>/dev/null || true)
|
||||||
|
[ -z "\$NCPASS" ] && { log "FOUT: kon dbpassword niet uitlezen uit AIO-container."; exit 1; }
|
||||||
|
log "AIO dbpassword aanwezig."
|
||||||
|
|
||||||
|
# ── 1. Staging-postgres dumpen ───────────────────────────────────────────────
|
||||||
|
log "1/6 Staging-postgres dumpen (--no-owner --no-acl)..."
|
||||||
|
sudo -u postgres pg_dump --no-owner --no-acl -Fp "\$STAGING_DB" > "\$DUMP"
|
||||||
|
log "Dump klaar: \$(du -sh \$DUMP | cut -f1)"
|
||||||
|
|
||||||
|
# ── 2. NC + notify-push stoppen ──────────────────────────────────────────────
|
||||||
|
log "2/6 NC + notify-push stoppen..."
|
||||||
|
docker stop "\$NC_CONTAINER" nextcloud-aio-notify-push 2>/dev/null || true
|
||||||
|
docker exec "\$DB_CONTAINER" psql -U "\$AIO_SUPER" -d postgres \
|
||||||
|
-c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity
|
||||||
|
WHERE datname = '\$AIO_DB';" 2>/dev/null || true
|
||||||
|
log "Containers gestopt."
|
||||||
|
|
||||||
|
# ── 3. AIO-database vervangen ────────────────────────────────────────────────
|
||||||
|
log "3/6 AIO DB droppen en opnieuw aanmaken..."
|
||||||
|
docker exec "\$DB_CONTAINER" psql -U "\$AIO_SUPER" -d postgres \
|
||||||
|
-c "DROP DATABASE IF EXISTS \${AIO_DB};"
|
||||||
|
docker exec "\$DB_CONTAINER" psql -U "\$AIO_SUPER" -d postgres \
|
||||||
|
-c "CREATE DATABASE \${AIO_DB} OWNER \${AIO_NCUSER}
|
||||||
|
ENCODING 'UTF8'
|
||||||
|
LC_COLLATE='en_US.utf8' LC_CTYPE='en_US.utf8'
|
||||||
|
TEMPLATE template0;"
|
||||||
|
|
||||||
|
log "4/6 Dump restoren als \$AIO_NCUSER..."
|
||||||
|
docker exec -e PGPASSWORD="\$NCPASS" -i "\$DB_CONTAINER" \
|
||||||
|
psql -U "\$AIO_NCUSER" "\$AIO_DB" < "\$DUMP"
|
||||||
|
|
||||||
|
# Verifieer eigenaarschap — alle tabellen moeten van oc_nextcloud zijn
|
||||||
|
OWNERS=\$(docker exec "\$DB_CONTAINER" psql -U "\$AIO_SUPER" "\$AIO_DB" \
|
||||||
|
-tAc "SELECT tableowner, count(*) FROM pg_tables
|
||||||
|
WHERE schemaname='public' GROUP BY tableowner;")
|
||||||
|
log "Tabeleigenaarschap na restore: \$OWNERS"
|
||||||
|
|
||||||
|
# ── 5. NC starten + config bijwerken ────────────────────────────────────────
|
||||||
|
log "5/6 NC starten + instanceid/passwordsalt/secret bijwerken..."
|
||||||
|
docker start "\$NC_CONTAINER"
|
||||||
|
for i in \$(seq 1 30); do
|
||||||
|
docker exec "\$NC_CONTAINER" php /var/www/html/occ status &>/dev/null && break || true
|
||||||
|
sleep 3
|
||||||
|
done
|
||||||
|
|
||||||
|
docker exec "\$NC_CONTAINER" php /var/www/html/occ \
|
||||||
|
config:system:set instanceid --value="\$INSTANCEID"
|
||||||
|
docker exec "\$NC_CONTAINER" php /var/www/html/occ \
|
||||||
|
config:system:set passwordsalt --value="\$PASSWORDSALT"
|
||||||
|
docker exec "\$NC_CONTAINER" php /var/www/html/occ \
|
||||||
|
config:system:set secret --value="\$SECRET"
|
||||||
|
docker exec "\$NC_CONTAINER" php /var/www/html/occ \
|
||||||
|
config:system:set datadirectory --value="/mnt/ncdata"
|
||||||
|
log "config.php bijgewerkt."
|
||||||
|
|
||||||
|
# ── 6. Repair + maintenance uit ─────────────────────────────────────────────
|
||||||
|
log "6/6 maintenance:repair + maintenance:mode --off..."
|
||||||
|
docker exec "\$NC_CONTAINER" php /var/www/html/occ \
|
||||||
|
maintenance:repair --include-expensive 2>&1 | tail -20
|
||||||
|
docker exec "\$NC_CONTAINER" php /var/www/html/occ maintenance:mode --off
|
||||||
|
|
||||||
|
# groupfolders herinstalleren (AIO start kaal, app zit in de DB maar niet in de container)
|
||||||
|
log "groupfolders installeren..."
|
||||||
|
docker exec "\$NC_CONTAINER" php /var/www/html/occ app:install groupfolders 2>&1 | tail -2 || \
|
||||||
|
docker exec "\$NC_CONTAINER" php /var/www/html/occ app:enable groupfolders 2>&1 | tail -2
|
||||||
|
|
||||||
|
docker exec "\$NC_CONTAINER" php /var/www/html/occ status
|
||||||
|
|
||||||
|
log "═══ AIO import klaar ═══"
|
||||||
|
log ""
|
||||||
|
log "Controleer NC via: https://${NC_DOMAIN}"
|
||||||
|
log ""
|
||||||
|
log "Volgende stappen:"
|
||||||
|
log " • Snapshot aanmaken: ssh <pve> 'qm snapshot <vmid> aio-import-ok'"
|
||||||
|
log " • Apps herinstalleren (zie output van 03-upgrade.sh / 04-install-aio.sh)"
|
||||||
|
log " • DNS-cutover naar productie-IP (als dat nog niet gebeurd is)"
|
||||||
|
log " • Contabo / bron uit maintenance halen na verificatie"
|
||||||
|
|
||||||
|
} 2>&1 | tee /var/log/aio-import.log
|
||||||
|
REMOTE
|
||||||
@@ -0,0 +1,252 @@
|
|||||||
|
# nextcloud-to-aio
|
||||||
|
|
||||||
|
Migration toolkit: Nextcloud bare/Hansson install → Nextcloud All-in-One (AIO).
|
||||||
|
|
||||||
|
Tested path: NC v30 (Debian 12, Apache + PHP-FPM + PostgreSQL) → AIO v13 (NC v33) on Debian 13.
|
||||||
|
Works for any NC version that can be upgraded sequentially to the AIO target version.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What it does
|
||||||
|
|
||||||
|
1. **Transfer** — rsync files + database dump from the old server to the new VM
|
||||||
|
2. **Build staging** — minimal Apache + PHP + DB stack to run the upgrade chain
|
||||||
|
3. **Upgrade** — sequential major-version upgrades (e.g. v30 → v31 → v32 → v33)
|
||||||
|
4. **Install AIO** — Docker + AIO mastercontainer; guided wizard
|
||||||
|
5. **Import** — restore the upgraded DB into AIO, carry over instanceid/passwordsalt/secret
|
||||||
|
|
||||||
|
The data directory (`/mnt/ncdata`) is rsynced once upfront and delta-synced at cutover.
|
||||||
|
Downtime window is only the final delta-sync + DNS cutover — typically under an hour.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- New VM with Debian 12/13, SSH root access, enough disk for data
|
||||||
|
- Old server accessible via SSH from the new VM (key-based)
|
||||||
|
- DNS control for your NC domain
|
||||||
|
- `~/.ssh/config` aliases set up for all hosts
|
||||||
|
|
||||||
|
Optional:
|
||||||
|
- Proxmox for live snapshots between upgrade steps (`USE_PROXMOX=true`)
|
||||||
|
- OPNSense with public VIP routing (`proxmox/build-vm.sh`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp config.sh config.local.sh # never commit this file
|
||||||
|
# edit config.local.sh — fill in all variables
|
||||||
|
source config.local.sh
|
||||||
|
|
||||||
|
./01-transfer.sh # start background rsync (can take hours)
|
||||||
|
./02-build-staging.sh # build Apache+PHP+DB on target
|
||||||
|
./03-upgrade.sh # sequential NC upgrade chain
|
||||||
|
./04-install-aio.sh test # AIO on staging domain first
|
||||||
|
# → complete wizard in browser at https://<target>:8080
|
||||||
|
./05-import.sh # import DB + data into AIO
|
||||||
|
|
||||||
|
# After verification:
|
||||||
|
./04-install-aio.sh production # switch to production domain + LE cert
|
||||||
|
./05-import.sh # re-run import on production AIO
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration reference (`config.sh`)
|
||||||
|
|
||||||
|
| Variable | Description |
|
||||||
|
|---|---|
|
||||||
|
| `SOURCE_HOST` | IP/hostname of the existing NC server |
|
||||||
|
| `SOURCE_NC_PATH` | Path to NC app directory (default `/var/www/nextcloud`) |
|
||||||
|
| `SOURCE_DATA_PATH` | Path to ncdata (default `/mnt/ncdata`) |
|
||||||
|
| `SOURCE_DB` | Database name on source |
|
||||||
|
| `SOURCE_DB_TYPE` | `postgres` or `mysql` |
|
||||||
|
| `SOURCE_DB_PASS` | DB password — only needed for MySQL |
|
||||||
|
| `TARGET_SSH` | SSH target for new VM (`root@10.x.x.x` or alias) |
|
||||||
|
| `TARGET_DATA_PATH` | Where ncdata lives on target |
|
||||||
|
| `NC_DOMAIN` | Production domain for AIO + Let's Encrypt |
|
||||||
|
| `NC_STAGING_DOMAIN` | Staging domain (HTTP only, internal) |
|
||||||
|
| `STAGING_DB_PASS` | Password for staging DB (created in step 02) |
|
||||||
|
| `NC_UPGRADE_VERSIONS` | Array of NC versions to upgrade through |
|
||||||
|
| `USE_PROXMOX` | `true`/`false` — enable snapshot after each upgrade step |
|
||||||
|
| `PROXMOX_HOST` | SSH alias for PVE node |
|
||||||
|
| `PROXMOX_VMID` | VM ID for snapshots |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MySQL → PostgreSQL
|
||||||
|
|
||||||
|
AIO uses PostgreSQL exclusively. If your source uses MySQL/MariaDB, you need one
|
||||||
|
extra step after the upgrade chain and before `05-import.sh`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On the target VM, after 03-upgrade.sh completes:
|
||||||
|
sudo -u www-data php8.3 /var/www/nextcloud/occ \
|
||||||
|
db:convert-type --all-apps pgsql \
|
||||||
|
<staging_db_user> <staging_db_pass> <staging_db_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
This converts the staging MariaDB to PostgreSQL in-place. After conversion,
|
||||||
|
`05-import.sh` takes a `pg_dump` of that PostgreSQL DB and imports it into AIO.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Upgrade path
|
||||||
|
|
||||||
|
NC requires sequential major-version upgrades — you cannot skip a major.
|
||||||
|
Set `NC_UPGRADE_VERSIONS` in `config.sh` based on your source version:
|
||||||
|
|
||||||
|
| Source | Example path |
|
||||||
|
|---|---|
|
||||||
|
| v28/v29 | `("29.0.x" "30.0.x" "31.0.x" "32.0.x" "33.0.x")` |
|
||||||
|
| v30 | `("31.0.14" "32.0.12" "33.0.6")` |
|
||||||
|
| v32 | `("33.0.6")` |
|
||||||
|
|
||||||
|
Check the [NC release archive](https://nextcloud.com/changelog/) for the latest patch version per major.
|
||||||
|
AIO ships a specific NC version — check the AIO release notes to know which final version to target.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Proxmox: creating the VM
|
||||||
|
|
||||||
|
If you use Proxmox, `proxmox/build-vm.sh` creates the target VM automatically:
|
||||||
|
Debian 13 cloud image, separate LVM data disk, loopback VIP (optional), OPNSense routing (optional).
|
||||||
|
|
||||||
|
Fill in the `Proxmox VM` section of `config.sh`, then:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./proxmox/build-vm.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
After the VM is up, set `TARGET_SSH` to match `VM_PRIVATE_IP` and continue with `01-transfer.sh`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Known issues & gotchas
|
||||||
|
|
||||||
|
These caused real failures during development. Read before running.
|
||||||
|
|
||||||
|
### PHP version
|
||||||
|
|
||||||
|
Debian 13 ships PHP 8.4. NC v30 requires PHP ≤ 8.3.
|
||||||
|
`02-build-staging.sh` adds the [sury.org](https://packages.sury.org) repo for PHP 8.3 automatically.
|
||||||
|
If your source is NC v32+, you can remove the sury.org step and use the system PHP.
|
||||||
|
|
||||||
|
### `rsync --exclude` anchoring
|
||||||
|
|
||||||
|
`--exclude='config/'` matches **any** directory named `config` in the tree,
|
||||||
|
including `apps/someapp/config/`. Use `--exclude='/config/'` (leading slash) to
|
||||||
|
anchor it to the root of the source — this is what the scripts use.
|
||||||
|
|
||||||
|
### `apc.enable_cli`
|
||||||
|
|
||||||
|
APCu is disabled in CLI by default (`apc.enable_cli=0`). `occ` commands that touch
|
||||||
|
APCu caches silently fail or produce wrong results. `02-build-staging.sh` sets
|
||||||
|
`apc.enable_cli=1` in `/etc/php/8.3/cli/php.ini` automatically.
|
||||||
|
|
||||||
|
### `require` vs `include` for config.php
|
||||||
|
|
||||||
|
NC's `config.php` defines `$CONFIG` but does not `return` it.
|
||||||
|
`include('/path/to/config.php')` returns `1` (bool), not the config array.
|
||||||
|
Use `require` instead — the scripts do this.
|
||||||
|
|
||||||
|
### Apps without `appinfo/info.xml`
|
||||||
|
|
||||||
|
Third-party apps that are present in `/var/www/nextcloud/apps/` but lack
|
||||||
|
`appinfo/info.xml` (abandoned, partially deleted, or leftover) cause `occ upgrade`
|
||||||
|
to crash. `03-upgrade.sh` removes them automatically before each upgrade step.
|
||||||
|
|
||||||
|
### AIO mastercontainer port binding
|
||||||
|
|
||||||
|
The AIO mastercontainer must **not** bind ports 80, 443, or 3478.
|
||||||
|
AIO's apache sub-container manages those ports itself.
|
||||||
|
Binding them on the mastercontainer causes the domain check to fail with a
|
||||||
|
conflict — two processes listening on port 443.
|
||||||
|
`04-install-aio.sh` does not bind those ports.
|
||||||
|
|
||||||
|
### AIO container names (v13+)
|
||||||
|
|
||||||
|
Older AIO docs refer to `nextcloud-aio-postgresql` — this was renamed.
|
||||||
|
Current names used by `05-import.sh`:
|
||||||
|
|
||||||
|
| Role | Container/resource name |
|
||||||
|
|---|---|
|
||||||
|
| DB container | `nextcloud-aio-database` |
|
||||||
|
| Database | `nextcloud_database` |
|
||||||
|
| NC DB user | `oc_nextcloud` |
|
||||||
|
| DB superuser | `nextcloud` |
|
||||||
|
|
||||||
|
### CPU spike on first AIO start after migration
|
||||||
|
|
||||||
|
When AIO starts for the first time with existing data, Elasticsearch indexes all
|
||||||
|
files, ClamAV downloads its virus database, and Recognize scans all photos.
|
||||||
|
On a large installation (500K+ files) this can take 1–2 hours at high CPU load.
|
||||||
|
|
||||||
|
**Plan this for late evening** — do not run the first AIO start during business
|
||||||
|
hours or when the server is serving other workloads.
|
||||||
|
|
||||||
|
### instanceid / passwordsalt / secret
|
||||||
|
|
||||||
|
These three values in `config.php` are the identity of the NC instance. They must
|
||||||
|
match what is in the database. `05-import.sh` reads them from the staging stack
|
||||||
|
and writes them into the AIO container's config. If the staging stack has been
|
||||||
|
removed before running `05-import.sh`, note these values from the source
|
||||||
|
`config.php` and set them manually.
|
||||||
|
|
||||||
|
### systemd-resolved blocking DNS
|
||||||
|
|
||||||
|
On Debian 12/13, `systemd-resolved` can intercept DNS and cause resolution
|
||||||
|
failures inside Docker containers. If containers cannot resolve hostnames:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl disable --now systemd-resolved
|
||||||
|
echo "nameserver 1.1.1.1" > /etc/resolv.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker DNS cache after IP change
|
||||||
|
|
||||||
|
Docker caches the upstream DNS from `/etc/resolv.conf` at daemon start.
|
||||||
|
After a VM IP change or network reconfiguration, containers may get DNS
|
||||||
|
timeouts even though the host resolves correctly. Fix:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl restart docker
|
||||||
|
docker start $(docker ps -aq)
|
||||||
|
```
|
||||||
|
|
||||||
|
### notify-push after import
|
||||||
|
|
||||||
|
`05-import.sh` stops `nextcloud-aio-notify-push` to release DB connections.
|
||||||
|
After import, restart it via the AIO admin UI or:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker start nextcloud-aio-notify-push
|
||||||
|
```
|
||||||
|
|
||||||
|
### OPNSense: fw01 root shell is csh
|
||||||
|
|
||||||
|
If you use OPNSense, its root shell is `csh`. Shell redirects like `2>/dev/null`
|
||||||
|
are interpreted differently by csh and can corrupt commands.
|
||||||
|
Always wrap non-trivial commands in `sh -c '...'` when SSHing to OPNSense.
|
||||||
|
`proxmox/build-vm.sh` does this for all fw01 commands.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## After import: apps to reinstall
|
||||||
|
|
||||||
|
`03-upgrade.sh` prints a list of apps that need manual reinstall after AIO import.
|
||||||
|
These are apps that were enabled in your source but are not included in AIO.
|
||||||
|
Common examples:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec nextcloud-aio-nextcloud php /var/www/html/occ app:install occweb
|
||||||
|
docker exec nextcloud-aio-nextcloud php /var/www/html/occ app:install drawio
|
||||||
|
docker exec nextcloud-aio-nextcloud php /var/www/html/occ app:install maps
|
||||||
|
docker exec nextcloud-aio-nextcloud php /var/www/html/occ app:install extract
|
||||||
|
docker exec nextcloud-aio-nextcloud php /var/www/html/occ app:enable notes
|
||||||
|
```
|
||||||
|
|
||||||
|
AIO includes natively (no action needed): fulltextsearch, notify_push, Talk/spreed,
|
||||||
|
whiteboard, recognize, files_antivirus (ClamAV), imaginary.
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# config.sh — Vul dit in voordat je de scripts draait.
|
||||||
|
# Wordt gesourcet door alle andere scripts: source "$(dirname "$0")/config.sh"
|
||||||
|
|
||||||
|
# ── Bronserver (bestaande Nextcloud-installatie) ──────────────────────────────
|
||||||
|
SOURCE_HOST="192.0.2.10" # IP van de oude NC-server
|
||||||
|
SOURCE_USER="root" # SSH-gebruiker (key-based, passwordless)
|
||||||
|
SOURCE_NC_PATH="/var/www/nextcloud"
|
||||||
|
SOURCE_DATA_PATH="/mnt/ncdata"
|
||||||
|
SOURCE_DB="nextcloud_db"
|
||||||
|
SOURCE_DB_USER="nextcloud_db_user"
|
||||||
|
SOURCE_DB_PASS="" # alleen nodig voor MySQL/MariaDB; leeglaten voor PostgreSQL
|
||||||
|
SOURCE_DB_TYPE="postgres" # "postgres" of "mysql"
|
||||||
|
|
||||||
|
# ── Doelserver (nieuwe VM waar AIO komt te draaien) ───────────────────────────
|
||||||
|
TARGET_SSH="root@10.0.0.10" # SSH naar doel (of ~/.ssh/config alias)
|
||||||
|
TARGET_DATA_PATH="/mnt/ncdata" # pad waar ncdata gemount is
|
||||||
|
|
||||||
|
# ── Domeinen ──────────────────────────────────────────────────────────────────
|
||||||
|
NC_DOMAIN="next.example.com" # productiedomein (voor AIO + LE-cert)
|
||||||
|
NC_STAGING_DOMAIN="nc.internal" # staging-domein (HTTP-only, intern)
|
||||||
|
|
||||||
|
# ── Staging PostgreSQL ────────────────────────────────────────────────────────
|
||||||
|
# Wachtwoord voor de staging-DB (wordt aangemaakt in 02-build-staging.sh)
|
||||||
|
STAGING_DB="nextcloud_db"
|
||||||
|
STAGING_DB_USER="nextcloud_db_user"
|
||||||
|
STAGING_DB_PASS="changeme-strong-password"
|
||||||
|
|
||||||
|
# ── NC upgrade-pad ────────────────────────────────────────────────────────────
|
||||||
|
# Pas aan op basis van je bronversie. NC vereist sequentiële upgrades per major.
|
||||||
|
# Eindversie moet overeenkomen met de AIO-versie die je installeert (zie stap 4).
|
||||||
|
# Voorbeeld: bron = v30, AIO target = v33
|
||||||
|
NC_UPGRADE_VERSIONS=("31.0.14" "32.0.12" "33.0.6")
|
||||||
|
|
||||||
|
# ── Proxmox snapshots (optioneel) ─────────────────────────────────────────────
|
||||||
|
# Zet op false als je geen Proxmox hebt of geen snapshots wil maken.
|
||||||
|
USE_PROXMOX=true
|
||||||
|
PROXMOX_HOST="pve01" # SSH-alias voor de PVE-node
|
||||||
|
PROXMOX_VMID=100 # VM ID van de doel-VM
|
||||||
|
|
||||||
|
# ── Proxmox VM aanmaken (alleen voor proxmox/build-vm.sh) ─────────────────────
|
||||||
|
# Sla deze sectie over als je de VM handmatig aanmaakt.
|
||||||
|
VM_NAME="nc-aio"
|
||||||
|
VM_CORES=4
|
||||||
|
VM_MEMORY=8192 # MB
|
||||||
|
VM_DISK_SIZE=50 # GB systeemdisk
|
||||||
|
VM_DATA_SIZE=500 # GB datadisk (/mnt/ncdata)
|
||||||
|
VM_VLAN=50 # VLAN-tag; leeglaten voor untagged
|
||||||
|
VM_BRIDGE=vmbr20
|
||||||
|
VM_PRIVATE_IP=10.0.0.10 # moet overeenkomen met TARGET_SSH
|
||||||
|
VM_PRIVATE_GW=10.0.0.1
|
||||||
|
VM_PRIVATE_PREFIX=24
|
||||||
|
VM_PUBLIC_IP="" # publiek VIP op loopback; leeglaten indien niet van toepassing
|
||||||
|
VM_DNS=10.0.0.1
|
||||||
|
VM_SEARCH_DOMAIN=example.com
|
||||||
|
CLOUD_IMAGE="/var/lib/vz/template/iso/debian-13-genericcloud-amd64.qcow2"
|
||||||
Executable
+210
@@ -0,0 +1,210 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# proxmox/build-vm.sh — Maakt de doel-VM aan op een Proxmox-node.
|
||||||
|
#
|
||||||
|
# Optioneel script: sla dit over als je de VM handmatig aanmaakt of een
|
||||||
|
# andere hypervisor gebruikt. De overige scripts (01–05) werken op elke
|
||||||
|
# Linux-VM met SSH-toegang.
|
||||||
|
#
|
||||||
|
# Wat het doet:
|
||||||
|
# - VM aanmaken (Debian 13 cloud image, qcow2)
|
||||||
|
# - Systeemdisk + aparte datadisk (LVM, ext4, /mnt/ncdata)
|
||||||
|
# - Netplan vervangen door /etc/network/interfaces
|
||||||
|
# - Loopback VIP (publiek IP) via systemd oneshot (indien VM_PUBLIC_IP gezet)
|
||||||
|
# - OPNSense: gateway + static route voor het publieke VIP (indien VM_PUBLIC_IP gezet)
|
||||||
|
#
|
||||||
|
# Vereisten:
|
||||||
|
# - SSH-alias voor PROXMOX_HOST en (indien VIP) fw01 werken
|
||||||
|
# - Debian 13 genericcloud qcow2 beschikbaar op de PVE-node (zie CLOUD_IMAGE)
|
||||||
|
# - config.sh ingevuld
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(dirname "$0")/../config.sh"
|
||||||
|
log() { echo "[$(date '+%H:%M:%S')] $*"; }
|
||||||
|
SCRATCHDIR=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$SCRATCHDIR"' EXIT
|
||||||
|
|
||||||
|
# ── STAP 1: VM aanmaken ───────────────────────────────────────────────────────
|
||||||
|
log "VM ${PROXMOX_VMID} (${VM_NAME}) aanmaken op ${PROXMOX_HOST}..."
|
||||||
|
ssh "${PROXMOX_HOST}" "
|
||||||
|
qm create ${PROXMOX_VMID} \
|
||||||
|
--name ${VM_NAME} \
|
||||||
|
--memory ${VM_MEMORY} \
|
||||||
|
--cores ${VM_CORES} \
|
||||||
|
--sockets 1 \
|
||||||
|
--cpu host \
|
||||||
|
--net0 virtio,bridge=${VM_BRIDGE}$([ -n '${VM_VLAN}' ] && echo ',tag=${VM_VLAN}' || true) \
|
||||||
|
--scsihw virtio-scsi-single \
|
||||||
|
--ostype l26 \
|
||||||
|
--serial0 socket \
|
||||||
|
--vga serial0 \
|
||||||
|
--agent enabled=1 \
|
||||||
|
--onboot 1
|
||||||
|
"
|
||||||
|
|
||||||
|
log "Systeemdisk importeren als qcow2..."
|
||||||
|
ssh "${PROXMOX_HOST}" \
|
||||||
|
"qm importdisk ${PROXMOX_VMID} ${CLOUD_IMAGE} local --format qcow2 2>&1 | tail -1"
|
||||||
|
|
||||||
|
PUBKEY_PVE=$(ssh "${PROXMOX_HOST}" \
|
||||||
|
"cat /root/.ssh/id_ed25519.pub 2>/dev/null || cat /root/.ssh/id_rsa.pub")
|
||||||
|
PUBKEY_WS=$(cat ~/.ssh/id_ed25519.pub 2>/dev/null || cat ~/.ssh/id_rsa.pub)
|
||||||
|
|
||||||
|
log "Disks koppelen + cloud-init configureren..."
|
||||||
|
ssh "${PROXMOX_HOST}" "
|
||||||
|
qm set ${PROXMOX_VMID} \
|
||||||
|
--scsi0 local:${PROXMOX_VMID}/vm-${PROXMOX_VMID}-disk-0.qcow2,discard=on,ssd=1,iothread=1 \
|
||||||
|
--scsi1 local:${VM_DATA_SIZE},format=qcow2,discard=on,iothread=1 \
|
||||||
|
--boot order=scsi0 \
|
||||||
|
--ide2 local:cloudinit \
|
||||||
|
--ciuser root \
|
||||||
|
--sshkeys <(printf '%s\n%s\n' '${PUBKEY_PVE}' '${PUBKEY_WS}') \
|
||||||
|
--ipconfig0 ip=${VM_PRIVATE_IP}/${VM_PRIVATE_PREFIX},gw=${VM_PRIVATE_GW} \
|
||||||
|
--nameserver ${VM_DNS} \
|
||||||
|
--searchdomain ${VM_SEARCH_DOMAIN}
|
||||||
|
qm resize ${PROXMOX_VMID} scsi0 ${VM_DISK_SIZE}G
|
||||||
|
"
|
||||||
|
|
||||||
|
# ── STAP 2: VM starten en wachten op SSH ─────────────────────────────────────
|
||||||
|
log "VM starten..."
|
||||||
|
ssh "${PROXMOX_HOST}" "qm start ${PROXMOX_VMID}"
|
||||||
|
log "Wachten op SSH (${VM_PRIVATE_IP})..."
|
||||||
|
for i in $(seq 1 60); do
|
||||||
|
ssh "${PROXMOX_HOST}" \
|
||||||
|
"ssh -o StrictHostKeyChecking=no -o ConnectTimeout=4 \
|
||||||
|
root@${VM_PRIVATE_IP} hostname 2>/dev/null" && break || true
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── STAP 3: Netplan → ifupdown ───────────────────────────────────────────────
|
||||||
|
log "Netplan vervangen door ifupdown..."
|
||||||
|
ssh "${PROXMOX_HOST}" "ssh root@${VM_PRIVATE_IP} bash -s" << EOF
|
||||||
|
set -e
|
||||||
|
apt-get install -y ifupdown 2>&1 | tail -1
|
||||||
|
cat > /etc/network/interfaces << 'IFEOF'
|
||||||
|
source /etc/network/interfaces.d/*
|
||||||
|
|
||||||
|
auto lo
|
||||||
|
iface lo inet loopback
|
||||||
|
|
||||||
|
auto ens18
|
||||||
|
iface ens18 inet static
|
||||||
|
address ${VM_PRIVATE_IP}/${VM_PRIVATE_PREFIX}
|
||||||
|
gateway ${VM_PRIVATE_GW}
|
||||||
|
dns-nameservers ${VM_DNS} 1.1.1.1
|
||||||
|
dns-search ${VM_SEARCH_DOMAIN}
|
||||||
|
IFEOF
|
||||||
|
systemctl disable systemd-networkd systemd-networkd-wait-online 2>/dev/null || true
|
||||||
|
mkdir -p /etc/cloud/cloud.cfg.d
|
||||||
|
echo "network: {config: disabled}" > /etc/cloud/cloud.cfg.d/99-disable-network.cfg
|
||||||
|
apt-get purge -y netplan.io 2>&1 | tail -1
|
||||||
|
rm -rf /etc/netplan /usr/share/netplan
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# ── STAP 4: Loopback VIP (optioneel) ─────────────────────────────────────────
|
||||||
|
if [[ -n "${VM_PUBLIC_IP}" ]]; then
|
||||||
|
log "Loopback VIP ${VM_PUBLIC_IP} instellen..."
|
||||||
|
ssh "${PROXMOX_HOST}" "ssh root@${VM_PRIVATE_IP} bash -s" << EOF
|
||||||
|
set -e
|
||||||
|
cat > /etc/systemd/system/loopback-vip.service << 'VIPEOF'
|
||||||
|
[Unit]
|
||||||
|
Description=Loopback VIP
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/sbin/ip addr add ${VM_PUBLIC_IP}/32 dev lo label lo:vip
|
||||||
|
ExecStop=/sbin/ip addr del ${VM_PUBLIC_IP}/32 dev lo label lo:vip
|
||||||
|
RemainAfterExit=yes
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
VIPEOF
|
||||||
|
systemctl enable loopback-vip.service
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── STAP 5: Datadisk — LVM + ext4 ───────────────────────────────────────────
|
||||||
|
log "Datadisk inrichten (LVM vg_data/ncdata, ext4, /mnt/ncdata)..."
|
||||||
|
ssh "${PROXMOX_HOST}" "ssh root@${VM_PRIVATE_IP} bash -s" << 'EOF'
|
||||||
|
set -e
|
||||||
|
apt-get install -y lvm2 2>&1 | tail -1
|
||||||
|
DISK=/dev/sdb
|
||||||
|
[ -b "$DISK" ] || { echo "FOUT: $DISK niet gevonden"; exit 1; }
|
||||||
|
[ -z "$(lsblk -dn -o FSTYPE $DISK 2>/dev/null)" ] || { echo "FOUT: $DISK is niet leeg"; exit 1; }
|
||||||
|
pvcreate "$DISK"
|
||||||
|
vgcreate vg_data "$DISK"
|
||||||
|
lvcreate -n ncdata -l 100%FREE vg_data
|
||||||
|
mkfs.ext4 -L ncdata /dev/vg_data/ncdata
|
||||||
|
mkdir -p /mnt/ncdata
|
||||||
|
echo "/dev/vg_data/ncdata /mnt/ncdata ext4 defaults,noatime 0 2" >> /etc/fstab
|
||||||
|
mount -a
|
||||||
|
df -h /mnt/ncdata
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# ── STAP 6: Basis-pakketten + reboot ─────────────────────────────────────────
|
||||||
|
log "Basis-pakketten installeren..."
|
||||||
|
ssh "${PROXMOX_HOST}" "ssh root@${VM_PRIVATE_IP} bash -s" << 'EOF'
|
||||||
|
set -e
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y qemu-guest-agent rsync curl ca-certificates gnupg htop 2>&1 | tail -1
|
||||||
|
systemctl enable --now qemu-guest-agent
|
||||||
|
EOF
|
||||||
|
|
||||||
|
log "Reboot..."
|
||||||
|
ssh "${PROXMOX_HOST}" "ssh root@${VM_PRIVATE_IP} reboot" || true
|
||||||
|
sleep 10
|
||||||
|
log "Wachten op herstart..."
|
||||||
|
for i in $(seq 1 60); do
|
||||||
|
ssh "${PROXMOX_HOST}" \
|
||||||
|
"ssh -o StrictHostKeyChecking=no -o ConnectTimeout=4 \
|
||||||
|
root@${VM_PRIVATE_IP} hostname 2>/dev/null" && break || true
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── STAP 7: OPNSense gateway + static route (optioneel) ──────────────────────
|
||||||
|
if [[ -n "${VM_PUBLIC_IP}" ]]; then
|
||||||
|
log "OPNSense: gateway + static route voor ${VM_PUBLIC_IP}..."
|
||||||
|
GW_NAME="GW_$(echo ${VM_NAME} | tr '[:lower:]' '[:upper:]')"
|
||||||
|
cat > "${SCRATCHDIR}/add_route.py" << PYEOF
|
||||||
|
import xml.etree.ElementTree as ET, uuid, sys
|
||||||
|
tree = ET.parse('/conf/config.xml')
|
||||||
|
root = tree.getroot()
|
||||||
|
name, gw_ip, pub_ip, iface = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]
|
||||||
|
|
||||||
|
gws = root.find('OPNsense/Gateways')
|
||||||
|
for old in gws.findall('gateway_item'):
|
||||||
|
if old.findtext('name') == name:
|
||||||
|
gws.remove(old)
|
||||||
|
gw = ET.SubElement(gws, 'gateway_item')
|
||||||
|
gw.set('uuid', str(uuid.uuid4()))
|
||||||
|
for tag, val in [
|
||||||
|
('disabled','0'),('name',name),('descr',name),
|
||||||
|
('interface',iface),('ipprotocol','inet'),('gateway',gw_ip),
|
||||||
|
('defaultgw','0'),('fargw','0'),('monitor_disable','1'),
|
||||||
|
('force_down','0'),('nosync','0'),('priority','255'),('weight','1')
|
||||||
|
]:
|
||||||
|
ET.SubElement(gw, tag).text = val
|
||||||
|
|
||||||
|
sr = root.find('staticroutes')
|
||||||
|
for old in sr.findall('route'):
|
||||||
|
if old.findtext('network') == pub_ip:
|
||||||
|
sr.remove(old)
|
||||||
|
route = ET.SubElement(sr, 'route')
|
||||||
|
route.set('uuid', str(uuid.uuid4()))
|
||||||
|
for tag, val in [('network',pub_ip),('gateway',name),('descr',name),('enabled','1')]:
|
||||||
|
ET.SubElement(route, tag).text = val
|
||||||
|
tree.write('/conf/config.xml')
|
||||||
|
print('done')
|
||||||
|
PYEOF
|
||||||
|
# fw01 root shell is csh — altijd wrappen in sh -c, anders verminkt csh
|
||||||
|
# fd-redirects (2>/dev/null) en kan een bogus 0.0.0.0/1 route aanmaken.
|
||||||
|
scp "${SCRATCHDIR}/add_route.py" fw01:/tmp/add_route.py
|
||||||
|
ssh fw01 "sh -c 'python3 /tmp/add_route.py \
|
||||||
|
${GW_NAME} ${VM_PRIVATE_IP} ${VM_PUBLIC_IP}/32 opt5 \
|
||||||
|
&& rm /tmp/add_route.py'"
|
||||||
|
ssh fw01 "sh -c 'configctl filter reload'"
|
||||||
|
log "✓ OPNSense route actief."
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "✓ VM klaar: ${VM_PRIVATE_IP}$([ -n '${VM_PUBLIC_IP}' ] && echo " / VIP ${VM_PUBLIC_IP}" || true)"
|
||||||
|
log " Pas TARGET_SSH aan in config.sh en ga verder met 01-transfer.sh"
|
||||||
Reference in New Issue
Block a user