docs+scripts: lock in nextcloud-as-PVE-NFS-browser surface
- docs/architecture/storage.md: new "Nextcloud as PVE-NFS browser" section documenting mount-per-archive + applicable_users model, why mount-level ACL beats Files Access Control on NC 30/31, the manifest shape (with current applicableUsers + enableSharing fields), and the trade-off - docs/runbooks/nextcloud-add-archive.md: 5-step runbook to surface a new directory under /srv/nfs/* to specific NC users via the bootstrap Job - scripts/anca-elements-sync.sh: deployed at /usr/local/bin/anca-elements-sync.sh on the PVE host; fpsync from Synology Anca/Elements to /srv/nfs/anca-elements (idempotent + resumable). The PVE replica is what the NC /anca-elements mount serves; the offsite-sync pipeline excludes this path (committed earlier this session) so we don't write it back to Synology NC usernames are admin/anca/emo (not display names — admin is Viktor). Stale "viktor" references in the manifest example dropped.
This commit is contained in:
parent
c624caf65a
commit
34f8c0f537
3 changed files with 201 additions and 0 deletions
112
scripts/anca-elements-sync.sh
Executable file
112
scripts/anca-elements-sync.sh
Executable file
|
|
@ -0,0 +1,112 @@
|
|||
#!/usr/bin/env bash
|
||||
# anca-elements-sync.sh — copy Anca's WD-Elements backup from Synology to PVE NFS
|
||||
#
|
||||
# Usage:
|
||||
# /usr/local/bin/anca-elements-sync.sh
|
||||
#
|
||||
# Idempotent: re-running after a successful sync is a no-op (only the dry-run
|
||||
# verification runs, which reports "sync verified clean" immediately).
|
||||
#
|
||||
# Resumable: if fpsync was interrupted, resume with:
|
||||
# fpsync -r /var/tmp/fpsync \
|
||||
# -n 4 -s 4G \
|
||||
# -o "-lptgoD -H --no-perms --no-owner --no-group --exclude=@eaDir/ --exclude=*@synoeastream --exclude=.DS_Store --exclude=Thumbs.db" \
|
||||
# /mnt/synology-backup/Anca/Elements/ /srv/nfs/anca-elements/
|
||||
#
|
||||
# NOTE: fpsync -o = rsync options override (what we want)
|
||||
# fpsync -O = fpart partition options override (NOT rsync)
|
||||
# NOTE: Do NOT use -a or -r in fpsync rsync options — fpsync handles
|
||||
# recursion via fpart; -r causes fpsync to warn and skip the slab.
|
||||
#
|
||||
# Log: /var/log/anca-elements-sync.log
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
LOG=/var/log/anca-elements-sync.log
|
||||
SRC_HOST=192.168.1.13
|
||||
SRC_EXPORT=/volume1/Backup
|
||||
SRC_SUBPATH=Anca/Elements
|
||||
MOUNT_POINT=/mnt/synology-backup
|
||||
DEST=/srv/nfs/anca-elements
|
||||
|
||||
log() {
|
||||
echo "[$(date -u '+%Y-%m-%dT%H:%M:%SZ')] $*" | tee -a "$LOG"
|
||||
}
|
||||
|
||||
# ── 1. Ensure destination + mount-point directories exist ────────────────────
|
||||
log "Step 1: ensuring directories"
|
||||
mkdir -p "$DEST" "$MOUNT_POINT"
|
||||
|
||||
# ── 2. NFS-mount Synology read-only (skip if already mounted) ───────────────
|
||||
MOUNTED_HERE=0
|
||||
if mountpoint -q "$MOUNT_POINT"; then
|
||||
log "Step 2: $MOUNT_POINT already mounted — skipping"
|
||||
else
|
||||
log "Step 2: mounting ${SRC_HOST}:${SRC_EXPORT} at $MOUNT_POINT (read-only)"
|
||||
mount -t nfs \
|
||||
-o ro,vers=4,nolock,soft,timeo=300,retrans=2 \
|
||||
"${SRC_HOST}:${SRC_EXPORT}" \
|
||||
"$MOUNT_POINT"
|
||||
MOUNTED_HERE=1
|
||||
log "Step 2: mount successful"
|
||||
fi
|
||||
|
||||
# ── 3. Ensure fpsync (from fpart package) is available ──────────────────────
|
||||
log "Step 3: checking for fpsync"
|
||||
if ! command -v fpsync >/dev/null 2>&1; then
|
||||
log "Step 3: fpsync not found — installing fpart"
|
||||
apt-get install -y fpart
|
||||
log "Step 3: fpart installed"
|
||||
else
|
||||
log "Step 3: fpsync already available"
|
||||
fi
|
||||
|
||||
# ── 4. Run fpsync (4-way parallel, no compression — source is already-compressed media) ──
|
||||
log "Step 4: starting fpsync"
|
||||
log " source : ${MOUNT_POINT}/${SRC_SUBPATH}/"
|
||||
log " dest : ${DEST}/"
|
||||
log " workers: 4, slab: 4G"
|
||||
fpsync \
|
||||
-n 4 \
|
||||
-s 4G \
|
||||
-o "-lptgoD -H --no-perms --no-owner --no-group --exclude=@eaDir/ --exclude=*@synoeastream --exclude=.DS_Store --exclude=Thumbs.db" \
|
||||
"${MOUNT_POINT}/${SRC_SUBPATH}/" \
|
||||
"${DEST}/" \
|
||||
2>&1 | tee -a "$LOG"
|
||||
log "Step 4: fpsync completed"
|
||||
|
||||
# ── 5. Verification dry-run ──────────────────────────────────────────────────
|
||||
log "Step 5: running dry-run verification rsync"
|
||||
VERIFY_OUT=$(rsync \
|
||||
-rlptgoD -H --no-perms --no-owner --no-group \
|
||||
--exclude='@eaDir/' --exclude='*@synoeastream' \
|
||||
--exclude='.DS_Store' --exclude='Thumbs.db' \
|
||||
-n --delete \
|
||||
--info=progress2 \
|
||||
--out-format='%o %f' \
|
||||
"${MOUNT_POINT}/${SRC_SUBPATH}/" \
|
||||
"${DEST}/" \
|
||||
2>&1 || true)
|
||||
|
||||
# Count lines that represent actual file changes (send / del. operations)
|
||||
CHANGE_COUNT=$(echo "$VERIFY_OUT" | grep -cE '^(send|del\.)' || true)
|
||||
|
||||
if [ "$CHANGE_COUNT" -eq 0 ]; then
|
||||
log "Step 5: sync verified clean — no pending changes"
|
||||
else
|
||||
log "Step 5: WARNING — verification found ${CHANGE_COUNT} pending change(s). First 50 lines:"
|
||||
# Use printf to avoid SIGPIPE from head closing the pipe early (set -o pipefail)
|
||||
{ echo "$VERIFY_OUT" | head -50; } >> "$LOG" 2>&1 || true
|
||||
fi
|
||||
|
||||
# ── 6. Unmount (only if we mounted it) ──────────────────────────────────────
|
||||
if [ "$MOUNTED_HERE" -eq 1 ]; then
|
||||
log "Step 6: unmounting $MOUNT_POINT"
|
||||
umount "$MOUNT_POINT"
|
||||
rmdir "$MOUNT_POINT"
|
||||
log "Step 6: unmounted"
|
||||
else
|
||||
log "Step 6: mount was pre-existing — leaving in place"
|
||||
fi
|
||||
|
||||
log "Done. Final size: $(du -sh "${DEST}" | cut -f1)"
|
||||
Loading…
Add table
Add a link
Reference in a new issue