Browse Source

e2e tests, unit tests + fixes

main
Nicolas Massé 2 weeks ago
parent
commit
2c08663a3a
  1. 91
      src/lib/core.sh
  2. 5
      test/e2e/cloud-init/standard-user-data
  3. 8
      test/e2e/cloud-init/with-fs-user-data
  4. 9
      test/e2e/cloud-init/with-zvol-user-data
  5. 496
      test/e2e/zvirt.bats
  6. 276
      test/unit/core.bats

91
src/lib/core.sh

@ -115,11 +115,6 @@ function parse_args () {
should_exit=1 should_exit=1
fi fi
if [ "$batch" -eq 1 ] && [ "$live" -ne 1 ]; then
echo "Error: Batch mode requires live snapshot mode."
should_exit=1
fi
if [[ ! "$snapshot_name" =~ ^[a-zA-Z0-9._-]+$ ]]; then if [[ ! "$snapshot_name" =~ ^[a-zA-Z0-9._-]+$ ]]; then
echo "Error: Snapshot name '$snapshot_name' contains invalid characters. Only alphanumeric characters, dots (.), underscores (_) and hyphens (-) are allowed." echo "Error: Snapshot name '$snapshot_name' contains invalid characters. Only alphanumeric characters, dots (.), underscores (_) and hyphens (-) are allowed."
should_exit=1 should_exit=1
@ -199,6 +194,12 @@ function domain_checks () {
state=$(domain_state "$domain") state=$(domain_state "$domain")
# Store those values in cache for later use
domain_params_cache["$domain/state"]="${state}"
domain_params_cache["$domain/dataset"]="${zfs_dataset}"
domain_params_cache["$domain/mountpoint"]="${zfs_mountpoint}"
domain_params_cache["$domain/zvols"]="${zfs_zvols[*]}"
case "$action" in case "$action" in
snapshot) snapshot)
# Check domain state # Check domain state
@ -223,7 +224,7 @@ function domain_checks () {
done done
# Check if save file already exists for live snapshot # Check if save file already exists for live snapshot
if [ -f "${zfs_mountpoint}/domain.save" ]; then if [ "$live" -eq 1 ] && has_save_file "$domain"; then
error "$domain: Save file '${zfs_mountpoint}/domain.save' already exists." ; error=1 error "$domain: Save file '${zfs_mountpoint}/domain.save' already exists." ; error=1
fi fi
;; ;;
@ -254,15 +255,10 @@ function domain_checks () {
return 1 return 1
fi fi
# Store those values in cache for later use
domain_params_cache["$domain/state"]="${state}"
domain_params_cache["$domain/dataset"]="${zfs_dataset}"
domain_params_cache["$domain/mountpoint"]="${zfs_mountpoint}"
domain_params_cache["$domain/zvols"]="${zfs_zvols[*]}"
return 0 return 0
} }
# Gets the mountpoint of the specified ZFS dataset.
function get_zfs_dataset_mountpoint () { function get_zfs_dataset_mountpoint () {
local zfs_dataset="$1" local zfs_dataset="$1"
zfs get -H -o value mountpoint "${zfs_dataset}" zfs get -H -o value mountpoint "${zfs_dataset}"
@ -393,6 +389,7 @@ function preflight_checks () {
return $error return $error
} }
# Removes the save file for the specified domain.
function remove_save_file () { function remove_save_file () {
local domain="$1" local domain="$1"
zfs_mountpoint="${domain_params_cache["$domain/mountpoint"]}" zfs_mountpoint="${domain_params_cache["$domain/mountpoint"]}"
@ -402,10 +399,61 @@ function remove_save_file () {
fi fi
} }
# Checks if the save file exists for the specified domain.
function has_save_file () {
local domain="$1"
zfs_mountpoint="${domain_params_cache["$domain/mountpoint"]}"
if [ -f "${zfs_mountpoint}/domain.save" ]; then
return 0
else
return 1
fi
}
# Thaws the specified domain filesystem.
function fsthaw_domain () {
local domain="$1"
virsh domfsthaw "$domain"
}
# Freezes the specified domain filesystem.
function fsfreeze_domain () {
local domain="$1"
virsh domfsfreeze "$domain"
}
# Thaws all domains in the list.
function fsthaw_all_domains () {
local domains=( "$@" )
for domain in "${domains[@]}"; do
log_verbose "$domain: Thawing domain..."
state="${domain_params_cache["$domain/state"]}"
if [ "$state" == "running" ]; then
fsthaw_domain "$domain"
fi
done
}
# Freezes all domains in the list.
function fsfreeze_all_domains () {
local domains=( "$@" )
for domain in "${domains[@]}"; do
log_verbose "$domain: Freezing domain..."
state="${domain_params_cache["$domain/state"]}"
if [ "$state" == "running" ]; then
fsfreeze_domain "$domain"
fi
done
}
# Takes snapshots for all specified domains. # Takes snapshots for all specified domains.
function take_snapshots () { function take_snapshots () {
if [ "$batch" -eq 1 ]; then if [ "$batch" -eq 1 ] && [ "$live" -eq 1 ]; then
pause_all_domains "${domains[@]}" pause_all_domains "${domains[@]}"
elif [ "$batch" -eq 1 ] && [ "$live" -eq 0 ]; then
fsfreeze_all_domains "${domains[@]}"
fi fi
for domain in "${domains[@]}"; do for domain in "${domains[@]}"; do
@ -417,12 +465,20 @@ function take_snapshots () {
remove_save_file "$domain" remove_save_file "$domain"
fi fi
else else
if [ "$batch" -eq 0 ] && [ "$state" == "running" ]; then
fsfreeze_domain "$domain"
fi
take_crash_consistent_snapshot "$domain" "$snapshot_name" take_crash_consistent_snapshot "$domain" "$snapshot_name"
if [ "$batch" -eq 0 ] && [ "$state" == "running" ]; then
fsthaw_domain "$domain"
fi
fi fi
done done
if [ "$batch" -eq 1 ]; then if [ "$batch" -eq 1 ] && [ "$live" -eq 1 ]; then
resume_all_domains "${domains[@]}" resume_all_domains "${domains[@]}"
elif [ "$batch" -eq 1 ] && [ "$live" -eq 0 ]; then
fsthaw_all_domains "${domains[@]}"
fi fi
return 0 return 0
@ -432,7 +488,12 @@ function take_snapshots () {
function revert_snapshots () { function revert_snapshots () {
for domain in "${domains[@]}"; do for domain in "${domains[@]}"; do
revert_snapshot "$domain" "$snapshot_name" revert_snapshot "$domain" "$snapshot_name"
restore_domain "$domain" if has_save_file "$domain"; then
restore_domain "$domain"
if [ "$batch" -eq 1 ]; then
remove_save_file "$domain"
fi
fi
done done
if [ "$batch" -eq 1 ]; then if [ "$batch" -eq 1 ]; then

5
test/e2e/cloud-init/standard-user-data

@ -3,9 +3,8 @@
bootcmd: bootcmd:
- setsebool -P virt_qemu_ga_run_unconfined on - setsebool -P virt_qemu_ga_run_unconfined on
- setsebool -P virt_qemu_ga_read_nonsecurity_files on - setsebool -P virt_qemu_ga_read_nonsecurity_files on
- setsebool -P virt_rw_qemu_ga_data on
runcmd: - install -o root -g root -m 0777 --context=system_u:object_r:virt_qemu_ga_data_t:s0 -d /test/rootfs
- install -o root -g root -m 0777 -d /test/rootfs
users: users:
- name: e2e - name: e2e

8
test/e2e/cloud-init/with-fs-user-data

@ -1,15 +1,13 @@
#cloud-config #cloud-config
mounts: mounts:
- [ data, /test/virtiofs, virtiofs, "defaults,nofail", "0", "0" ] - [ data, /test/virtiofs, virtiofs, "defaults,context=system_u:object_r:virt_qemu_ga_data_t:s0", "0", "0" ]
bootcmd: bootcmd:
- setsebool -P virt_qemu_ga_run_unconfined on - setsebool -P virt_qemu_ga_run_unconfined on
- setsebool -P virt_qemu_ga_read_nonsecurity_files on - setsebool -P virt_qemu_ga_read_nonsecurity_files on
- setsebool -P virt_rw_qemu_ga_data on
runcmd: - install -o root -g root -d /test/virtiofs
- install -o root -g root -m 0777 -d /test/virtiofs
- mount -a
users: users:
- name: e2e - name: e2e

9
test/e2e/cloud-init/with-zvol-user-data

@ -13,16 +13,13 @@ fs_setup:
partition: auto partition: auto
mounts: mounts:
- [ LABEL=zvol, /test/zvol, xfs, "defaults,nofail", "0", "2" ] - [ LABEL=zvol, /test/zvol, xfs, "defaults,context=system_u:object_r:virt_qemu_ga_data_t:s0", "0", "2" ]
bootcmd: bootcmd:
- setsebool -P virt_qemu_ga_run_unconfined on - setsebool -P virt_qemu_ga_run_unconfined on
- setsebool -P virt_qemu_ga_read_nonsecurity_files on - setsebool -P virt_qemu_ga_read_nonsecurity_files on
- setsebool -P virt_rw_qemu_ga_data on
runcmd: - mkdir -p /test/zvol
- install -o root -g root -m 0777 -d /test/zvol
- chmod 0777 /test/zvol
- mount -a
users: users:
- name: e2e - name: e2e

496
test/e2e/zvirt.bats

@ -11,6 +11,13 @@ setup() {
"${BATS_TEST_DIRNAME}/../../src/zvirt" "$@" "${BATS_TEST_DIRNAME}/../../src/zvirt" "$@"
} }
declare -g e2e_test_enable_debug=0
e2e_test_debug_log(){
if [ "$e2e_test_enable_debug" -eq 1 ]; then
echo "$@" >&3
fi
}
qemu_exec() { qemu_exec() {
domain="$1" domain="$1"
shift || true shift || true
@ -23,17 +30,17 @@ setup() {
done done
local command="{\"execute\": \"guest-exec\", \"arguments\": {\"path\": \"$1\", \"arg\": [ $json_args ], \"capture-output\": true }}" local command="{\"execute\": \"guest-exec\", \"arguments\": {\"path\": \"$1\", \"arg\": [ $json_args ], \"capture-output\": true }}"
output="$(virsh qemu-agent-command "$domain" "$command")" output="$(virsh qemu-agent-command "$domain" "$command")"
#echo "qemu_exec: command output: $output" >&3 #e2e_test_debug_log "qemu_exec: command output: $output"
pid="$(echo "$output" | jq -r '.return.pid')" pid="$(echo "$output" | jq -r '.return.pid')"
if [ -z "$pid" ] || [ "$pid" == "null" ]; then if [ -z "$pid" ] || [ "$pid" == "null" ]; then
echo "qemu_exec: failed to get pid from command output" >&3 e2e_test_debug_log "qemu_exec: failed to get pid from command output"
return 1 return 1
fi fi
sleep .25 sleep .25
while true; do while true; do
local status_command="{\"execute\": \"guest-exec-status\", \"arguments\": {\"pid\": $pid}}" local status_command="{\"execute\": \"guest-exec-status\", \"arguments\": {\"pid\": $pid}}"
status_output="$(virsh qemu-agent-command "$domain" "$status_command")" status_output="$(virsh qemu-agent-command "$domain" "$status_command")"
#echo "qemu_exec: status output: $status_output" >&3 #e2e_test_debug_log "qemu_exec: status output: $status_output"
exited="$(echo "$status_output" | jq -r '.return.exited')" exited="$(echo "$status_output" | jq -r '.return.exited')"
if [ "$exited" == "true" ]; then if [ "$exited" == "true" ]; then
stdout_base64="$(echo "$status_output" | jq -r '.return["out-data"]')" stdout_base64="$(echo "$status_output" | jq -r '.return["out-data"]')"
@ -79,20 +86,31 @@ EOF
} }
cleanup() { cleanup() {
echo "teardown: Cleaning up created domains and images..." >&3 e2e_test_debug_log "teardown: Cleaning up created domains and images..."
for domain in standard with-fs with-zvol; do for domain in standard with-fs with-zvol; do
state="$(virsh domstate "$domain" 2>/dev/null || true)"
if [[ -n "$state" && "$state" != "shut off" ]]; then
virsh destroy "$domain"
fi
if virsh dominfo "$domain" &>/dev/null; then if virsh dominfo "$domain" &>/dev/null; then
virsh destroy "$domain" || true virsh undefine "$domain" --nvram
virsh undefine "$domain" --nvram || true
fi fi
zfs destroy -r data/domains/"$domain" || true done
sleep 1
sync
sleep 1
for domain in standard with-fs with-zvol; do
if zfs list data/domains/"$domain" &>/dev/null; then
zfs destroy -rR data/domains/"$domain"
fi
sleep .2
rm -rf "/var/lib/libvirt/images/${domain}" rm -rf "/var/lib/libvirt/images/${domain}"
done done
} }
create_domains() { create_domains() {
# Create the standard VM # Create the standard VM
echo "setup: Creating the standard VM..." >&3 e2e_test_debug_log "setup: Creating the standard VM..."
mkdir -p /var/lib/libvirt/images/standard mkdir -p /var/lib/libvirt/images/standard
zfs create -p data/domains/standard -o mountpoint=/var/lib/libvirt/images/standard zfs create -p data/domains/standard -o mountpoint=/var/lib/libvirt/images/standard
convert_cloud_image "$fedora_img" "/var/lib/libvirt/images/standard/root.img" convert_cloud_image "$fedora_img" "/var/lib/libvirt/images/standard/root.img"
@ -113,7 +131,7 @@ EOF
--boot=uefi --boot=uefi
# Create the with-fs VM # Create the with-fs VM
echo "setup: Creating the with-fs VM..." >&3 e2e_test_debug_log "setup: Creating the with-fs VM..."
mkdir -p /var/lib/libvirt/images/with-fs /srv/with-fs mkdir -p /var/lib/libvirt/images/with-fs /srv/with-fs
chmod 0777 /srv/with-fs chmod 0777 /srv/with-fs
zfs create -p data/domains/with-fs -o mountpoint=/var/lib/libvirt/images/with-fs zfs create -p data/domains/with-fs -o mountpoint=/var/lib/libvirt/images/with-fs
@ -138,7 +156,7 @@ EOF
--filesystem=type=mount,accessmode=passthrough,driver.type=virtiofs,driver.queue=1024,source.dir=/srv/with-fs,target.dir=data --filesystem=type=mount,accessmode=passthrough,driver.type=virtiofs,driver.queue=1024,source.dir=/srv/with-fs,target.dir=data
# Create the with-zvol VM # Create the with-zvol VM
echo "setup: Creating the with-zvol VM..." >&3 e2e_test_debug_log "setup: Creating the with-zvol VM..."
mkdir -p /var/lib/libvirt/images/with-zvol mkdir -p /var/lib/libvirt/images/with-zvol
zfs create -p data/domains/with-zvol -o mountpoint=/var/lib/libvirt/images/with-zvol zfs create -p data/domains/with-zvol -o mountpoint=/var/lib/libvirt/images/with-zvol
zfs create -V 10G data/domains/with-zvol/data zfs create -V 10G data/domains/with-zvol/data
@ -162,31 +180,39 @@ EOF
} }
readiness_wait() { readiness_wait() {
echo "setup: Waiting for VMs to become ready..." >&3 e2e_test_debug_log "setup: Waiting for VMs to become ready..."
for domain in standard with-fs with-zvol; do for domain in standard with-fs with-zvol; do
echo "setup: Waiting for qemu guest agent to be running in domain '$domain'..." >&3 e2e_test_debug_log "setup: Waiting for qemu guest agent to be running in domain '$domain'..."
until virsh qemu-agent-command "$domain" '{"execute":"guest-ping"}' &>/dev/null; do until virsh qemu-agent-command "$domain" '{"execute":"guest-ping"}' &>/dev/null; do
sleep 2 sleep 2
done done
done done
echo "setup: all VMs started successfully" >&3 e2e_test_debug_log "setup: all VMs started successfully"
for domain in standard with-fs with-zvol; do for domain in standard with-fs with-zvol; do
echo "setup: Waiting for cloud-init to complete in domain '$domain'..." >&3 e2e_test_debug_log "setup: Waiting for cloud-init to complete in domain '$domain'..."
until qemu_exec "$domain" test -f /var/lib/cloud/instance/boot-finished; do until qemu_exec "$domain" test -f /var/lib/cloud/instance/boot-finished; do
sleep 2 sleep 2
done done
done done
echo "setup: VMs are ready" >&3 if ! qemu_exec with-fs grep -q /test/virtiofs /proc/mounts; then
e2e_test_debug_log "setup: virtiofs not mounted in 'with-fs' domain"
return 1
fi
if ! qemu_exec with-zvol grep -q /test/zvol /proc/mounts; then
e2e_test_debug_log "setup: zvol not mounted in 'with-zvol' domain"
return 1
fi
e2e_test_debug_log "setup: VMs are ready"
} }
local fedora_url="https://download.fedoraproject.org/pub/fedora/linux/releases/42/Cloud/x86_64/images/Fedora-Cloud-Base-Generic-42-1.1.x86_64.qcow2" local fedora_url="https://download.fedoraproject.org/pub/fedora/linux/releases/42/Cloud/x86_64/images/Fedora-Cloud-Base-Generic-42-1.1.x86_64.qcow2"
local fedora_img="/var/lib/libvirt/images/$(basename "$fedora_url")" local fedora_img="/var/lib/libvirt/images/$(basename "$fedora_url")"
if [ ! -f "$fedora_img" ]; then if [ ! -f "$fedora_img" ]; then
echo "setup: downloading Fedora Cloud image to $fedora_img" >&3 e2e_test_debug_log "setup: downloading Fedora Cloud image to $fedora_img"
mkdir -p /var/lib/libvirt/images/library mkdir -p /var/lib/libvirt/images/library
curl -sSfL -o "$fedora_img" "$fedora_url" curl -sSfL -o "$fedora_img" "$fedora_url"
fi fi
echo "setup: Fedora Cloud image is at $fedora_img" >&3 e2e_test_debug_log "setup: Fedora Cloud image is at $fedora_img"
# Cleanup any leftover artifacts from previous runs # Cleanup any leftover artifacts from previous runs
cleanup cleanup
@ -199,15 +225,18 @@ teardown() {
} }
@test "zvirt: setup selftest" { @test "zvirt: setup selftest" {
echo "setup: provisioning completed" >&3 e2e_test_debug_log "setup: provisioning completed"
} }
@test "zvirt: take live snapshot in batch mode" { @test "zvirt: take live snapshot in batch mode" {
# Create witness files in all three domains before taking snapshots # Create witness files in all three domains before taking snapshots
qemu_exec standard touch /test/rootfs/before-backup1 qemu_exec standard touch /test/rootfs/witness-file
qemu_exec with-fs touch /test/rootfs/before-backup1 qemu_exec with-fs touch /test/virtiofs/witness-file
qemu_exec with-zvol touch /test/zvol/before-backup1 qemu_exec with-zvol touch /test/zvol/witness-file
[[ -f /srv/with-fs/before-backup1 ]]
# Verify that the witness files exist in the virtiofs host mount
run test -f /srv/with-fs/witness-file
assert_success
# Take live snapshots for all three domains # Take live snapshots for all three domains
run zvirt snapshot -b -d standard -d with-zvol -d with-fs -s backup1 -l run zvirt snapshot -b -d standard -d with-zvol -d with-fs -s backup1 -l
@ -227,13 +256,13 @@ teardown() {
# Assert that the files created before the snapshot exist # Assert that the files created before the snapshot exist
run qemu_exec standard ls -1 /test/rootfs run qemu_exec standard ls -1 /test/rootfs
assert_success assert_success
assert_output "before-backup1" assert_output "witness-file"
run qemu_exec with-fs ls -1 /test/rootfs run qemu_exec with-fs ls -1 /test/virtiofs
assert_success assert_success
assert_output "before-backup1" assert_output "witness-file"
run qemu_exec with-zvol ls -1 /test/zvol run qemu_exec with-zvol ls -1 /test/zvol
assert_success assert_success
assert_output "before-backup1" assert_output "witness-file"
# List snapshots and verify their existence # List snapshots and verify their existence
run zvirt list -d standard -d with-zvol -d with-fs run zvirt list -d standard -d with-zvol -d with-fs
@ -253,25 +282,408 @@ Snapshots for domain 'with-fs':
assert_output --partial "with-zvol:" assert_output --partial "with-zvol:"
assert_output --partial "with-fs:" assert_output --partial "with-fs:"
assert_output --partial "Pre-flight checks failed." assert_output --partial "Pre-flight checks failed."
# Delete the witness files
run qemu_exec standard rm /test/rootfs/witness-file
assert_success
run qemu_exec with-fs rm /test/virtiofs/witness-file
assert_success
run qemu_exec with-zvol rm /test/zvol/witness-file
assert_success
# Sync all filesystems
run qemu_exec standard sync
assert_success
run qemu_exec with-fs sync
assert_success
run qemu_exec with-zvol sync
assert_success
# Verify that the witness files have been deleted in the virtiofs host mount
run test -f /srv/with-fs/witness-file
assert_failure
# Stop all domains
run virsh destroy standard
assert_success
run virsh destroy with-fs
assert_success
run virsh destroy with-zvol
assert_success
# Revert snapshots in batch mode
run zvirt revert -b -d standard -d with-zvol -d with-fs -s backup1
assert_success
# Check all domains are running again
run virsh domstate standard
assert_success
assert_output "running"
run virsh domstate with-fs
assert_success
assert_output "running"
run virsh domstate with-zvol
assert_success
assert_output "running"
# Verify that the witness files still exist after revert
run qemu_exec standard ls -1 /test/rootfs
assert_success
assert_output "witness-file"
run qemu_exec with-fs ls -1 /test/virtiofs
assert_success
assert_output "witness-file"
run qemu_exec with-zvol ls -1 /test/zvol
assert_success
assert_output "witness-file"
} }
@test "zvirt: take live snapshot without batch mode" {
# Create witness files in all three domains before taking snapshots
qemu_exec standard touch /test/rootfs/witness-file
qemu_exec with-fs touch /test/virtiofs/witness-file
qemu_exec with-zvol touch /test/zvol/witness-file
# Verify that the witness files exist in the virtiofs host mount
run test -f /srv/with-fs/witness-file
assert_success
# @test "call_parse_args: take a crash-consistent snapshot for two domains" { # Take live snapshots for all three domains
# run zvirt snapshot -d standard -d with-zvol -d with-fs backup2 run zvirt snapshot -d standard -d with-zvol -d with-fs -s backup1 -l
# assert_success assert_success
# }
# @test "call_parse_args: revert snapshot for a domain" { # Verify that the domains are still running
# virsh destroy standard || true run virsh domstate standard
# run zvirt revert -d standard -s backup2 assert_success
# assert_success assert_output "running"
# } run virsh domstate with-fs
assert_success
assert_output "running"
run virsh domstate with-zvol
assert_success
assert_output "running"
# @test "call_parse_args: revert snapshot for all domains in batch mode" { # Assert that the files created before the snapshot exist
# virsh destroy standard || true run qemu_exec standard ls -1 /test/rootfs
# virsh destroy with-zvol || true assert_success
# virsh destroy with-fs || true assert_output "witness-file"
# run zvirt revert -b -d standard -d with-zvol -d with-fs -s backup1 run qemu_exec with-fs ls -1 /test/virtiofs
# assert_success assert_success
# } assert_output "witness-file"
run qemu_exec with-zvol ls -1 /test/zvol
assert_success
assert_output "witness-file"
# List snapshots and verify their existence
run zvirt list -d standard -d with-zvol -d with-fs
assert_success
assert_output "Snapshots for domain 'standard':
- backup1
Snapshots for domain 'with-zvol':
- backup1
Snapshots for domain 'with-fs':
- backup1"
# Attempt to take the same snapshot again and expect failure
run zvirt snapshot -d standard -d with-zvol -d with-fs -s backup1 -l
assert_failure
assert_output --partial "Snapshot 'backup1' already exists."
assert_output --partial "standard:"
assert_output --partial "with-zvol:"
assert_output --partial "with-fs:"
assert_output --partial "Pre-flight checks failed."
# Delete the witness files
run qemu_exec standard rm /test/rootfs/witness-file
assert_success
run qemu_exec with-fs rm /test/virtiofs/witness-file
assert_success
run qemu_exec with-zvol rm /test/zvol/witness-file
assert_success
# Sync all filesystems
run qemu_exec standard sync
assert_success
run qemu_exec with-fs sync
assert_success
run qemu_exec with-zvol sync
assert_success
# Verify that the witness files have been deleted in the virtiofs host mount
run test -f /srv/with-fs/witness-file
assert_failure
# Stop all domains
run virsh destroy standard
assert_success
run virsh destroy with-fs
assert_success
run virsh destroy with-zvol
assert_success
# Revert snapshots in batch mode
run zvirt revert -d standard -d with-zvol -d with-fs -s backup1
assert_success
# Check all domains are running again
run virsh domstate standard
assert_success
assert_output "running"
run virsh domstate with-fs
assert_success
assert_output "running"
run virsh domstate with-zvol
assert_success
assert_output "running"
# Verify that the witness files still exist after revert
run qemu_exec standard ls -1 /test/rootfs
assert_success
assert_output "witness-file"
run qemu_exec with-fs ls -1 /test/virtiofs
assert_success
assert_output "witness-file"
run qemu_exec with-zvol ls -1 /test/zvol
assert_success
assert_output "witness-file"
}
@test "zvirt: take crash-consistent snapshot without batch mode" {
# Create witness files in all three domains before taking snapshots
qemu_exec standard touch /test/rootfs/witness-file
qemu_exec with-fs touch /test/virtiofs/witness-file
qemu_exec with-zvol touch /test/zvol/witness-file
# Verify that the witness files exist in the virtiofs host mount
run test -f /srv/with-fs/witness-file
assert_success
# Take crash-consistent snapshots for all three domains
run zvirt snapshot -d standard -d with-zvol -d with-fs -s backup1
assert_success
# Verify that the domains are still running
run virsh domstate standard
assert_success
assert_output "running"
run virsh domstate with-fs
assert_success
assert_output "running"
run virsh domstate with-zvol
assert_success
assert_output "running"
# Assert that the files created before the snapshot exist
run qemu_exec standard ls -1 /test/rootfs
assert_success
assert_output "witness-file"
run qemu_exec with-fs ls -1 /test/virtiofs
assert_success
assert_output "witness-file"
run qemu_exec with-zvol ls -1 /test/zvol
assert_success
assert_output "witness-file"
# List snapshots and verify their existence
run zvirt list -d standard -d with-zvol -d with-fs
assert_success
assert_output "Snapshots for domain 'standard':
- backup1
Snapshots for domain 'with-zvol':
- backup1
Snapshots for domain 'with-fs':
- backup1"
# Attempt to take the same snapshot again and expect failure
run zvirt snapshot -d standard -d with-zvol -d with-fs -s backup1
assert_failure
assert_output --partial "Snapshot 'backup1' already exists."
assert_output --partial "standard:"
assert_output --partial "with-zvol:"
assert_output --partial "with-fs:"
assert_output --partial "Pre-flight checks failed."
# Delete the witness files
run qemu_exec standard rm /test/rootfs/witness-file
assert_success
run qemu_exec with-fs rm /test/virtiofs/witness-file
assert_success
run qemu_exec with-zvol rm /test/zvol/witness-file
assert_success
# Sync all filesystems
run qemu_exec standard sync
assert_success
run qemu_exec with-fs sync
assert_success
run qemu_exec with-zvol sync
assert_success
# Wait a moment to ensure all writes are flushed
sleep 2
# Verify that the witness files have been deleted in the virtiofs host mount
run test -f /srv/with-fs/witness-file
assert_failure
# Stop all domains
run virsh destroy standard
assert_success
run virsh destroy with-fs
assert_success
run virsh destroy with-zvol
assert_success
# Revert snapshots in batch mode
run zvirt revert -d standard -d with-zvol -d with-fs -s backup1
assert_success
# Check all domains have been shut off
run virsh domstate standard
assert_success
assert_output "shut off"
run virsh domstate with-fs
assert_success
assert_output "shut off"
run virsh domstate with-zvol
assert_success
assert_output "shut off"
# Start all domains
run virsh start standard
assert_success
run virsh start with-fs
assert_success
run virsh start with-zvol
assert_success
# Wait for all domains to be fully ready
readiness_wait
# Verify that the witness files still exist after revert
run qemu_exec standard ls -1 /test/rootfs
assert_success
assert_output "witness-file"
run qemu_exec with-fs ls -1 /test/virtiofs
assert_success
assert_output "witness-file"
run qemu_exec with-zvol ls -1 /test/zvol
assert_success
assert_output "witness-file"
}
@test "zvirt: take crash-consistent snapshot with batch mode" {
# Create witness files in all three domains before taking snapshots
qemu_exec standard touch /test/rootfs/witness-file
qemu_exec with-fs touch /test/virtiofs/witness-file
qemu_exec with-zvol touch /test/zvol/witness-file
# Verify that the witness files exist in the virtiofs host mount
run test -f /srv/with-fs/witness-file
assert_success
# Take crash-consistent snapshots for all three domains
run zvirt snapshot -b -d standard -d with-zvol -d with-fs -s backup1
assert_success
# Verify that the domains are still running
run virsh domstate standard
assert_success
assert_output "running"
run virsh domstate with-fs
assert_success
assert_output "running"
run virsh domstate with-zvol
assert_success
assert_output "running"
# Assert that the files created before the snapshot exist
run qemu_exec standard ls -1 /test/rootfs
assert_success
assert_output "witness-file"
run qemu_exec with-fs ls -1 /test/virtiofs
assert_success
assert_output "witness-file"
run qemu_exec with-zvol ls -1 /test/zvol
assert_success
assert_output "witness-file"
# List snapshots and verify their existence
run zvirt list -d standard -d with-zvol -d with-fs
assert_success
assert_output "Snapshots for domain 'standard':
- backup1
Snapshots for domain 'with-zvol':
- backup1
Snapshots for domain 'with-fs':
- backup1"
# Attempt to take the same snapshot again and expect failure
run zvirt snapshot -b -d standard -d with-zvol -d with-fs -s backup1
assert_failure
assert_output --partial "Snapshot 'backup1' already exists."
assert_output --partial "standard:"
assert_output --partial "with-zvol:"
assert_output --partial "with-fs:"
assert_output --partial "Pre-flight checks failed."
# Delete the witness files
run qemu_exec standard rm /test/rootfs/witness-file
assert_success
run qemu_exec with-fs rm /test/virtiofs/witness-file
assert_success
run qemu_exec with-zvol rm /test/zvol/witness-file
assert_success
# Sync all filesystems
run qemu_exec standard sync
assert_success
run qemu_exec with-fs sync
assert_success
run qemu_exec with-zvol sync
assert_success
# Wait a moment to ensure all writes are flushed
sleep 2
# Verify that the witness files have been deleted in the virtiofs host mount
run test -f /srv/with-fs/witness-file
assert_failure
# Stop all domains
run virsh destroy standard
assert_success
run virsh destroy with-fs
assert_success
run virsh destroy with-zvol
assert_success
# Revert snapshots in batch mode
run zvirt revert -b -d standard -d with-zvol -d with-fs -s backup1
assert_success
# Check all domains are running again
run virsh domstate standard
assert_success
assert_output "running"
run virsh domstate with-fs
assert_success
assert_output "running"
run virsh domstate with-zvol
assert_success
assert_output "running"
# Wait for all domains to be fully ready
readiness_wait
# Verify that the witness files still exist after revert
run qemu_exec standard ls -1 /test/rootfs
assert_success
assert_output "witness-file"
run qemu_exec with-fs ls -1 /test/virtiofs
assert_success
assert_output "witness-file"
run qemu_exec with-zvol ls -1 /test/zvol
assert_success
assert_output "witness-file"
}

276
test/unit/core.bats

@ -165,6 +165,23 @@ snapshot2"
assert_failure assert_failure
} }
@test "has_save_file: nominal case" {
# Temporary directory for save files
local temp_dir="$(mktemp -d)"
mkdir -p "$temp_dir/foo" "$temp_dir/bar"
# Only foo has a save file
touch "$temp_dir/foo/domain.save"
# Fill up the cache
declare -A domain_params_cache=( ["foo/mountpoint"]="$temp_dir/foo" ["bar/mountpoint"]="$temp_dir/bar" )
# Run the test
run in_bash has_save_file foo
assert_success
run in_bash has_save_file bar
assert_failure
}
@test "take_live_snapshot: nominal case" { @test "take_live_snapshot: nominal case" {
# Mock the underlying tools # Mock the underlying tools
declare -A domain_params_cache=( ["foo/state"]="running" ["foo/dataset"]="data/domains/foo" ["foo/mountpoint"]="/var/lib/libvirt/images/foo" ["foo/zvols"]="" ) declare -A domain_params_cache=( ["foo/state"]="running" ["foo/dataset"]="data/domains/foo" ["foo/mountpoint"]="/var/lib/libvirt/images/foo" ["foo/zvols"]="" )
@ -330,6 +347,78 @@ data/domains/baz/virtiofs"
[[ "$(mock_get_call_num ${virsh_mock})" -eq 2 ]] [[ "$(mock_get_call_num ${virsh_mock})" -eq 2 ]]
} }
@test "fsthaw_all_domains: nominal case" {
# Mock the underlying tools
local domains=( "foo" "bar" )
declare -A domain_params_cache=( ["foo/state"]="running" ["bar/state"]="shut off" )
fsthaw_mock="$(mock_create)"
fsthaw_domain() {
if [[ "$*" == "foo" ]]; then
$fsthaw_mock "$@"
return $?
fi
return 1
}
export -f fsthaw_domain
export fsthaw_mock
# Run the test
run in_bash fsthaw_all_domains "${domains[@]}"
assert_success
[[ "$(mock_get_call_num ${fsthaw_mock})" -eq 1 ]]
}
@test "fsfreeze_all_domains: nominal case" {
# Mock the underlying tools
local domains=( "foo" "bar" )
declare -A domain_params_cache=( ["foo/state"]="running" ["bar/state"]="shut off" )
fsfreeze_mock="$(mock_create)"
fsfreeze_domain() {
if [[ "$*" == "foo" ]]; then
$fsfreeze_mock "$@"
return $?
fi
return 1
}
export -f fsfreeze_domain
export fsfreeze_mock
# Run the test
run in_bash fsfreeze_all_domains "${domains[@]}"
assert_success
[[ "$(mock_get_call_num ${fsfreeze_mock})" -eq 1 ]]
}
@test "fsthaw_domain: nominal case" {
# Mock the underlying tools
virsh() {
[[ "$*" == "domfsthaw foo" ]] && return 0
return 1
}
export -f virsh
# Run the test
run in_bash virsh domfsthaw "foo"
assert_success
run in_bash virsh domfsthaw "bar"
assert_failure
}
@test "fsfreeze_domain: nominal case" {
# Mock the underlying tools
virsh() {
[[ "$*" == "domfsfreeze foo" ]] && return 0
return 1
}
export -f virsh
# Run the test
run in_bash virsh domfsfreeze "foo"
assert_success
run in_bash virsh domfsfreeze "bar"
assert_failure
}
@test "domain_checks: nominal case" { @test "domain_checks: nominal case" {
# Mock the underlying tools # Mock the underlying tools
domain_exists() { domain_exists() {
@ -393,6 +482,24 @@ data/domains/baz/virtiofs"
assert_success assert_success
run in_bash domain_checks revert bar backup1 run in_bash domain_checks revert bar backup1
assert_success assert_success
# Live mode with existing save file
has_save_file() {
return 0
}
export -f has_save_file
live=1
run in_bash domain_checks snapshot foo backup2
assert_failure
# Live mode with non-existing save file
has_save_file() {
return 1
}
export -f has_save_file
live=1
run in_bash domain_checks snapshot foo backup2
assert_success
} }
@test "list_snapshots: nominal case" { @test "list_snapshots: nominal case" {
@ -451,7 +558,21 @@ snapshot2"
restore_domain() { return 1; } restore_domain() { return 1; }
resume_all_domains() { return 1; } resume_all_domains() { return 1; }
remove_save_file() { return 1; } remove_save_file() { return 1; }
export -f take_crash_consistent_snapshot pause_all_domains take_live_snapshot restore_domain resume_all_domains remove_save_file fsfreeze_all_domains() { return 1; }
fsthaw_all_domains() { return 1; }
fsfreeze_domain() {
if [[ "$*" == "foo" ]]; then
return 0
fi
return 1
}
fsthaw_domain() {
if [[ "$*" == "foo" ]]; then
return 0
fi
return 1
}
export -f take_crash_consistent_snapshot pause_all_domains take_live_snapshot restore_domain resume_all_domains remove_save_file fsfreeze_all_domains fsthaw_all_domains fsfreeze_domain fsthaw_domain
declare -A domain_params_cache=( ["foo/state"]="running" ["bar/state"]="shut off" ) declare -A domain_params_cache=( ["foo/state"]="running" ["bar/state"]="shut off" )
@ -478,29 +599,26 @@ snapshot2"
fi fi
return 1 return 1
} }
pause_all_domains() { pause_all_domains() { return 1; }
if [[ "$*" == "foo bar" ]]; then
return 0
fi
return 1
}
take_live_snapshot() { return 1; } take_live_snapshot() { return 1; }
restore_domain() { return 1; } restore_domain() { return 1; }
resume_all_domains() { resume_all_domains() { return 1; }
remove_save_file() { return 1; }
fsfreeze_all_domains() {
if [[ "$*" == "foo bar" ]]; then if [[ "$*" == "foo bar" ]]; then
return 0 return 0
fi fi
return 1 return 1
} }
remove_save_file() { fsthaw_all_domains() {
regex="^(foo|bar) backup$" if [[ "$*" == "foo bar" ]]; then
if [[ "$*" =~ $regex ]]; then
return 0 return 0
fi fi
return 1 return 1
} }
export -f take_crash_consistent_snapshot pause_all_domains take_live_snapshot restore_domain resume_all_domains remove_save_file fsfreeze_domain() { return 1; }
fsthaw_domain() { return 1; }
export -f take_crash_consistent_snapshot pause_all_domains take_live_snapshot restore_domain resume_all_domains remove_save_file fsfreeze_all_domains fsthaw_all_domains fsfreeze_domain fsthaw_domain
declare -A domain_params_cache=( ["foo/state"]="running" ["bar/state"]="shut off" ) declare -A domain_params_cache=( ["foo/state"]="running" ["bar/state"]="shut off" )
@ -541,7 +659,86 @@ snapshot2"
} }
resume_all_domains() { return 1; } resume_all_domains() { return 1; }
remove_save_file() { return 1; } remove_save_file() { return 1; }
export -f take_crash_consistent_snapshot pause_all_domains take_live_snapshot restore_domain resume_all_domains remove_save_file fsfreeze_all_domains() { return 1; }
fsthaw_all_domains() { return 1; }
fsfreeze_domain() {
if [[ "$*" == "bar" ]]; then
return 0
fi
return 1
}
fsthaw_domain() {
if [[ "$*" == "bar" ]]; then
return 0
fi
return 1
}
export -f take_crash_consistent_snapshot pause_all_domains take_live_snapshot restore_domain resume_all_domains remove_save_file fsfreeze_all_domains fsthaw_all_domains fsfreeze_domain fsthaw_domain
declare -A domain_params_cache=( ["foo/state"]="running" ["bar/state"]="shut off" )
# Run the test
domains=( "foo" "bar" )
snapshot_name="backup"
batch=0
live=1
run in_bash take_snapshots
assert_success
# Add a non-existing domain to the list
domains+=( "baz" )
run in_bash take_snapshots
assert_failure
}
@test "take_snapshots: batch=1, live=1" {
# Mock the underlying tools
take_crash_consistent_snapshot() {
if [[ "$*" == "bar backup" ]]; then
return 0
fi
return 1
}
pause_all_domains() {
if [[ "$*" == "foo bar" ]]; then
return 0
fi
return 1
}
take_live_snapshot() {
if [[ "$*" == "foo backup" ]]; then
return 0
fi
return 1
}
restore_domain() {
if [[ "$*" == "foo" ]]; then
return 0
fi
return 1
}
resume_all_domains() {
if [[ "$*" == "foo bar" ]]; then
return 0
fi
return 1
}
remove_save_file() { return 1; }
fsfreeze_all_domains() { return 1; }
fsthaw_all_domains() { return 1; }
fsfreeze_domain() {
if [[ "$*" == "bar" ]]; then
return 0
fi
return 1
}
fsthaw_domain() {
if [[ "$*" == "bar" ]]; then
return 0
fi
return 1
}
export -f take_crash_consistent_snapshot pause_all_domains take_live_snapshot restore_domain resume_all_domains remove_save_file fsfreeze_all_domains fsthaw_all_domains fsfreeze_domain fsthaw_domain
declare -A domain_params_cache=( ["foo/state"]="running" ["bar/state"]="shut off" ) declare -A domain_params_cache=( ["foo/state"]="running" ["bar/state"]="shut off" )
@ -569,15 +766,31 @@ snapshot2"
return 1 return 1
} }
restore_domain() { restore_domain() {
regex="^(foo|bar)$" if [[ "$*" == "foo" ]]; then
if [[ "$*" =~ $regex ]]; then
return 0 return 0
fi fi
return 1 return 1
} }
resume_all_domains() { return 1; } resume_all_domains() { return 1; }
export -f revert_snapshot restore_domain resume_all_domains has_save_file() {
if [[ "$*" == "foo" ]]; then
return 0
fi
return 1
}
remove_save_file() { return 1; }
domain_state() {
if [[ "$*" == "foo" ]]; then
echo "paused"
return 0
elif [[ "$*" == "bar" ]]; then
echo "shut off"
return 0
fi
return 1
}
export -f revert_snapshot restore_domain resume_all_domains has_save_file remove_save_file domain_state
# Run the test # Run the test
domains=( "foo" "bar" ) domains=( "foo" "bar" )
snapshot_name="backup" snapshot_name="backup"
@ -601,8 +814,7 @@ snapshot2"
return 1 return 1
} }
restore_domain() { restore_domain() {
regex="^(foo|bar)$" if [[ "$*" == "foo" ]]; then
if [[ "$*" =~ $regex ]]; then
return 0 return 0
fi fi
return 1 return 1
@ -613,7 +825,29 @@ snapshot2"
fi fi
return 1 return 1
} }
export -f revert_snapshot restore_domain resume_all_domains has_save_file() {
if [[ "$*" == "foo" ]]; then
return 0
fi
return 1
}
remove_save_file() {
if [[ "$*" == "foo" ]]; then
return 0
fi
return 1
}
domain_state() {
if [[ "$*" == "foo" ]]; then
echo "paused"
return 0
elif [[ "$*" == "bar" ]]; then
echo "shut off"
return 0
fi
return 1
}
export -f revert_snapshot restore_domain resume_all_domains has_save_file remove_save_file domain_state
# Run the test # Run the test
domains=( "foo" "bar" ) domains=( "foo" "bar" )

Loading…
Cancel
Save