weizhouapache commented on code in PR #14386:
URL: https://github.com/apache/cloudstack/pull/14386#discussion_r4359619973
##########
scripts/vm/hypervisor/kvm/nasbackup.sh:
##########
@@ -137,6 +138,282 @@ get_linstor_uuid_from_device() {
return 1
}
+# Globals populated by parse_rbd_opts() for the disk it was last called with.
+RBD_IMAGE="" # "<pool>/<image>"
+RBD_ARGS=() # connection arguments to pass to every rbd invocation
+RBD_KEYFILE="" # temporary cephx keyfile, removed by rbd_cleanup_keyfile
+# Prefix NASBackupProvider names every chain anchor with. Snapshots carrying
it belong to this
+# tool and no other, which is what makes the stale-anchor sweep safe.
+RBD_SNAPSHOT_PREFIX="csbackup-"
+
+rbd_cleanup_keyfile() {
+ if [[ -n "$RBD_KEYFILE" ]]; then
+ rm -f "$RBD_KEYFILE"
+ RBD_KEYFILE=""
+ fi
+}
+
+# Split an RBD disk string produced by KVMPhysicalDisk.RBDStringBuilder into
the image name and
+# the connection arguments the rbd CLI needs:
+#
rbd:<pool>/<image>:mon_host=<h1>\;<h2>:auth_client_required=cephx:id=<user>:key=<secret>:...
+# ':' and ';' are backslash-escaped inside mon_host so that option splitting
stays unambiguous;
+# they are masked before the split and restored afterwards.
+parse_rbd_opts() {
+ local s="${1#rbd:}"
+ s="${s//\\:/$'\x01'}"
+ s="${s//\\;/$'\x02'}"
+ local -a parts=()
+ IFS=':' read -ra parts <<< "$s"
+
+ rbd_cleanup_keyfile
+ RBD_IMAGE="${parts[0]}"
+ RBD_ARGS=()
+
+ local p monHost="" authId="" authKey=""
+ for p in "${parts[@]:1}"; do
+ case "$p" in
+ mon_host=*) monHost="${p#mon_host=}" ;;
+ id=*) authId="${p#id=}" ;;
+ key=*) authKey="${p#key=}" ;;
+ esac
+ done
+ monHost="${monHost//$'\x01'/:}" # host\:port -> host:port
+ monHost="${monHost//$'\x02'/,}" # monitor list -> the comma-separated form
rbd -m accepts
+
+ if [[ -n "$monHost" ]]; then
+ RBD_ARGS+=(-m "$monHost")
+ fi
+ if [[ -n "$authId" ]]; then
+ RBD_ARGS+=(--id "$authId")
+ if [[ -n "$authKey" ]]; then
+ RBD_KEYFILE=$(mktemp -t csbackup-ceph.XXXXXX)
+ chmod 600 "$RBD_KEYFILE"
+ printf '%s' "$authKey" > "$RBD_KEYFILE"
+ RBD_ARGS+=(--keyfile "$RBD_KEYFILE")
+ fi
+ fi
+}
+
+# Best-effort removal of snapshot $1 from each of the remaining disk
arguments. Used both to roll
+# back a half-created anchor and to drop the previous anchor once its delta
has been exported.
+rbd_remove_snapshots() {
+ local snapName="$1"
+ shift
+ local d
+ for d in "$@"; do
+ [[ -z "$d" ]] && continue
+ parse_rbd_opts "$d"
+ if ! rbd "${RBD_ARGS[@]}" snap rm "$RBD_IMAGE@$snapName" >> "$logFile"
2>&1; then
+ log -e "cleanup: failed to remove RBD snapshot $RBD_IMAGE@$snapName
(non-fatal)"
+ fi
+ done
+ rbd_cleanup_keyfile
+}
+
+# Remove every snapshot carrying the backup prefix except $1, across the
remaining disk arguments.
+# Only snapshots this tool created are ever touched: user and CloudStack
volume snapshots do not
+# use the prefix.
+rbd_sweep_stale_anchors() {
+ local keepSnap="$1"
+ shift
+ local d stale
+ for d in "$@"; do
+ [[ -z "$d" ]] && continue
+ parse_rbd_opts "$d"
+ while read -r stale; do
+ [[ -z "$stale" || "$stale" == "$keepSnap" ]] && continue
+ if ! rbd "${RBD_ARGS[@]}" snap rm "$RBD_IMAGE@$stale" >> "$logFile"
2>&1; then
+ log -e "cleanup: failed to remove stale RBD snapshot $RBD_IMAGE@$stale
(non-fatal)"
+ fi
+ done < <(
+ rbd "${RBD_ARGS[@]}" snap ls "$RBD_IMAGE" --format json 2>>"$logFile" |
python3 -c '
+import sys, json
+try:
+ snaps = json.load(sys.stdin)
+except Exception:
+ sys.exit(0)
+for s in snaps or []:
+ name = (s or {}).get("name") or ""
+ if name.startswith(sys.argv[1]):
+ print(name)
+' "$RBD_SNAPSHOT_PREFIX" 2>/dev/null || true
+ )
+ done
+ rbd_cleanup_keyfile
+}
+
+rbd_snapshot_exists() {
+ local snapName="$1"
+ rbd "${RBD_ARGS[@]}" snap ls "$RBD_IMAGE" --format json 2>>"$logFile" |
python3 -c '
+import sys, json
+try:
+ snaps = json.load(sys.stdin)
+except Exception:
+ sys.exit(1)
+sys.exit(0 if any((s or {}).get("name") == sys.argv[1] for s in snaps or [])
else 1)
+' "$snapName"
+}
+
+# Incremental backup of Ceph/RBD volumes.
+#
+# RBD images are raw, so they have nowhere to persist a QEMU dirty bitmap and
cannot take part in
+# the libvirt-checkpoint scheme backup_running_vm uses. Ceph provides the
equivalent natively: an
+# `rbd snap` per cycle as the anchor, and `rbd export-diff --from-snap` to
stream out exactly the
+# extents that changed between two anchors (computed inside Ceph from the
image object-map).
+#
+# Because the snapshot lives in Ceph rather than in the QEMU process, this
works identically for
+# running and stopped VMs, and survives a live migration of the VM between
hosts.
+#
+# Only the newest anchor is kept on each image: once this run's delta is
exported, the parent
+# snapshot is removed. A full additionally sweeps every other snapshot
carrying the backup prefix,
+# so anchors abandoned by a deleted chain, an unassigned offering or a restore
cannot accumulate.
+backup_rbd_vm() {
+ if [[ -z "$DISK_PATHS" ]]; then
+ echo "RBD backup mode requires the volume paths (-d|--diskpaths)"
+ exit 1
+ fi
+ if [[ -z "$BITMAP_NEW" ]]; then
+ echo "RBD backup mode requires --bitmap-new (the snapshot to create)"
+ exit 1
+ fi
+
+ local effective_mode="$MODE"
+ case "$effective_mode" in
+ full|incremental)
+ ;;
+ *)
+ echo "Mode $effective_mode is not supported by the rbd method"
+ cleanup
+ exit 1
+ ;;
+ esac
+ local -a disks=()
+ IFS=',' read -ra disks <<< "$DISK_PATHS"
+
+ local d
+ for d in "${disks[@]}"; do
+ if [[ "$d" != rbd:* ]]; then
+ echo "RBD backup mode was requested but disk $d is not an RBD volume"
+ exit 1
+ fi
+ done
+
+ # The parent anchor must still be present on EVERY disk. A volume migration
or an out-of-band
+ # snapshot purge can drop it; fall back to a full rather than letting
export-diff fail. Checked
+ # before anything is mounted or created so the fallback costs nothing.
+ if [[ "$effective_mode" == "incremental" ]]; then
+ for d in "${disks[@]}"; do
+ parse_rbd_opts "$d"
+ if ! rbd_snapshot_exists "$BITMAP_PARENT"; then
+ log -e "rbd incremental: parent snapshot $BITMAP_PARENT missing on
$RBD_IMAGE — falling back to full"
+ echo "INCREMENTAL_FALLBACK=true"
+ effective_mode="full"
+ break
+ fi
+ done
+ rbd_cleanup_keyfile
+ fi
+
+ mount_operation
+ mkdir -p "$dest" || { echo "Failed to create backup directory $dest"; exit
1; }
+
+ # Freezing the guest filesystems makes the snapshot set
filesystem-consistent instead of merely
+ # crash-consistent. Only possible while the VM runs and its guest agent
answers.
+ local thaw=0
+ local vm_state
+ vm_state=$(virsh -c qemu:///system domstate "$VM" 2>/dev/null || true)
+ if [[ ${QUIESCE} == "true" && "$vm_state" == "running" ]]; then
+ if virsh -c qemu:///system qemu-agent-command "$VM"
'{"execute":"guest-fsfreeze-freeze"}' > /dev/null 2>/dev/null; then
+ thaw=1
+ fi
+ fi
+
+ # Create this cycle's anchor on every disk back to back, so the set is as
close to a single
+ # point in time for the VM as a whole as Ceph allows.
+ local -a snapped=()
+ local snap_failed=0
+ for d in "${disks[@]}"; do
Review Comment:
:+1:
updated, if thaw is 0 , pause and resume
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]