Skip to content

Commit c4f5b53

Browse files
committed
#263: add --no-sparse-detection option
1 parent 4982365 commit c4f5b53

18 files changed

+503
-19
lines changed
+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: nosparsedetect CI on ubuntu-22.04
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
test:
7+
8+
runs-on: ubuntu-22.04
9+
10+
steps:
11+
- uses: actions/checkout@v4
12+
- uses: abbbi/github-actions-tune@v1
13+
- name: Set up libvirt
14+
run: |
15+
sudo apt-get update
16+
sudo apt-get install -y \
17+
apparmor-profiles \
18+
bridge-utils \
19+
dnsmasq-base \
20+
ebtables \
21+
libarchive-tools \
22+
libguestfs-tools \
23+
libvirt-clients \
24+
libvirt-daemon \
25+
libvirt-daemon-system \
26+
qemu-kvm \
27+
qemu-utils \
28+
python3-libnbd \
29+
python3-tqdm \
30+
python3-lz4 \
31+
python3-libvirt \
32+
python3-lxml \
33+
python3-paramiko\
34+
python3-scp \
35+
python3-colorlog \
36+
nbdkit \
37+
nbdkit-plugin-python \
38+
unzip \
39+
libnbd-bin \
40+
;
41+
# start daemon
42+
echo 'security_driver = "none"' | sudo tee -a /etc/libvirt/qemu.conf
43+
sudo aa-teardown
44+
sudo rm -f /etc/apparmor.d/libvirt/libvirt*
45+
sudo systemctl start libvirtd
46+
sudo systemctl restart libvirtd
47+
sudo modprobe nbd max_partitions=10
48+
- name: Execute tests (nosparsedetect)
49+
run: cd t && sudo -E make nosparsedetect.tests

Changelog

+15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
Version 2.24
2+
---------
3+
* virnbdbackup:
4+
- Add option --no-sparse-detection (#263): The feature implemented as
5+
with version 2.21 requires to query the complete base:allocation
6+
bitmap during incremental backup.
7+
8+
Depending on the Hypervisor hardware and the virtual machine disk size,
9+
querying the base:allocation bitmap may take longer than to simply treat
10+
fstrimmed blocks the same way as changed blocks and include them in the
11+
backup.
12+
13+
The option introduced can be used to disable the new behavior and can
14+
enhance backup times for specific users.
15+
116
Version 2.23
217
---------
318
* virtnbdbackup:

README.md

+20
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ of your `kvm/qemu` virtual machines.
7373
- [Backup fails with "Timed out during operation: cannot acquire state change lock"](#backup-fails-with-timed-out-during-operation-cannot-acquire-state-change-lock)
7474
- [Backup fails with "Failed to bind socket to /var/tmp/virtnbdbackup.XX: Permission denied"](#backup-fails-with-failed-to-bind-socket-to-vartmpvirtnbdbackupxx-permission-denied)
7575
- [High memory usage during backup](#high-memory-usage-during-backup)
76+
- [fstrim and (incremental) backup sizes](#fstrim-and-incremental-backup-sizes)
7677
- [Test your backups!](#test-your-backups)
7778
- [Links](#links)
7879

@@ -1220,6 +1221,25 @@ version if using `virtnbdbackup` on any other distribution.
12201221

12211222
See also: https://github.com/abbbi/virtnbdbackup/issues/8
12221223

1224+
## fstrim and (incremental) backup sizes
1225+
1226+
If virtual machines have configured disks with discard option and fstrim is
1227+
running frequently, trimmed blocks are detected during backup operation by
1228+
default.
1229+
1230+
If, for example, the fstrim operation frees 30 GiB of disk space, and the
1231+
virtual machine has only 5 GiB of changed data blocks, 30 GiB of data
1232+
will be skipped during incremental backup.
1233+
1234+
This works by comparing the complete allocation bitmap of the virtual machine
1235+
disk images during incremental backup.
1236+
1237+
Depending on your hardware and the size of the virtual disks, the operation to
1238+
query the complete allocation bitmap during incremental backup, may take longer
1239+
than to backup the complete changeset. Therefore, you can disable the detection
1240+
of sparse/fstrimmed blocks using the `--no-sparse-detection` option.
1241+
1242+
12231243
## Test your backups!
12241244

12251245
The utility is provided "as is", i take no responsibility or warranty if you

libvirtnbdbackup/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515
along with this program. If not, see <https://www.gnu.org/licenses/>.
1616
"""
1717

18-
__version__ = "2.23"
18+
__version__ = "2.24"

libvirtnbdbackup/backup/disk.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,14 @@ def _getExtentHandler(args: Namespace, nbdClient):
5454
if args.qemu:
5555
logging.info("Using qemu tools to query extents")
5656
extentHandler = extenthandler.ExtentHandler(
57-
qemu.util(nbdClient.cType.exportName), nbdClient.cType
57+
qemu.util(nbdClient.cType.exportName),
58+
nbdClient.cType,
59+
args.no_sparse_detection,
5860
)
5961
else:
60-
extentHandler = extenthandler.ExtentHandler(nbdClient, nbdClient.cType)
62+
extentHandler = extenthandler.ExtentHandler(
63+
nbdClient, nbdClient.cType, args.no_sparse_detection
64+
)
6165

6266
return extentHandler
6367

libvirtnbdbackup/backup/server.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def connect( # pylint: disable=too-many-arguments
7575
else:
7676
cType = nbdcli.Unix(disk.target, metaContext, socket)
7777

78-
nbdClient = nbdcli.client(cType)
78+
nbdClient = nbdcli.client(cType, args.no_sparse_detection)
7979

8080
try:
8181
return nbdClient.connect()

libvirtnbdbackup/extenthandler/extenthandler.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class ExtentHandler:
3232
extent information as nbdinfo or qemu-img map
3333
"""
3434

35-
def __init__(self, nbdFh, cType) -> None:
35+
def __init__(self, nbdFh, cType, no_sparse_detection: bool) -> None:
3636
self.useQemu = False
3737
self._maxRequestBlock: int = 4294967295
3838
self._align: int = 512
@@ -44,6 +44,7 @@ def __init__(self, nbdFh, cType) -> None:
4444
self._nbdFh = nbdFh
4545
self._cType = cType
4646
self._extentEntries: Dict = {}
47+
self.no_sparse_detection = no_sparse_detection
4748

4849
if cType.metaContext == "":
4950
self._metaContext = CONTEXT_BASE_ALLOCATION
@@ -55,9 +56,12 @@ def __init__(self, nbdFh, cType) -> None:
5556
log.debug("NBD server exports [%d] metacontexts:", contexts)
5657
for i in range(0, contexts):
5758
ctx = self._nbdFh.nbd.get_meta_context(i)
59+
if self.no_sparse_detection is True and ctx == CONTEXT_BASE_ALLOCATION:
60+
continue
5861
self._extentEntries[ctx] = []
5962
else:
60-
self._extentEntries[CONTEXT_BASE_ALLOCATION] = []
63+
if self.no_sparse_detection is False:
64+
self._extentEntries[CONTEXT_BASE_ALLOCATION] = []
6165
self._extentEntries[self._metaContext] = []
6266

6367
log.debug("Primary meta context for backup: %s", self._metaContext)
@@ -264,6 +268,10 @@ def queryBlockStatus(self) -> List[Extent]:
264268
extObj.offset,
265269
extObj.offset + extObj.length,
266270
)
271+
if self.no_sparse_detection is True:
272+
log.info("Skipping detection of sparse/fstrimmed blocks.")
273+
return extentList
274+
267275
if self._metaContext != CONTEXT_BASE_ALLOCATION:
268276
log.debug("Detected [%d] bytes of changed data regions.", totalLength)
269277
extentList = self.overlap(extentList)

libvirtnbdbackup/nbdcli/client.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@
2424
log = logging.getLogger("nbd")
2525

2626

27+
# pylint: disable=too-many-instance-attributes
2728
class client:
2829
"""Helper functions for NBD"""
2930

30-
def __init__(self, cType):
31+
def __init__(self, cType, no_sparse_detection: bool):
3132
"""
3233
Connect NBD backend
3334
"""
@@ -40,6 +41,7 @@ def __init__(self, cType):
4041
self._metaContext = nbd.CONTEXT_BASE_ALLOCATION
4142
self.maxRequestSize = 33554432
4243
self.minRequestSize = 65536
44+
self.no_sparse_detection = no_sparse_detection
4345
self.nbd = nbd.NBD()
4446

4547
def debug(func, args):
@@ -72,7 +74,8 @@ def _connect(self) -> nbd.NBD:
7274
try:
7375
if self.cType.tls:
7476
self.nbd.set_tls(nbd.TLS_REQUIRE)
75-
self.nbd.add_meta_context(nbd.CONTEXT_BASE_ALLOCATION)
77+
if self.no_sparse_detection is False:
78+
self.nbd.add_meta_context(nbd.CONTEXT_BASE_ALLOCATION)
7679
if self._metaContext != "":
7780
log.debug(
7881
"Adding meta context to NBD connection: [%s]", self._metaContext

libvirtnbdbackup/restore/server.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def setup(args: Namespace, exportName: str, targetFile: str, virtClient: virt.cl
4848
proc = qFh.startRemoteRestoreNbdServer(args, targetFile)
4949
cType = nbdcli.TCP(exportName, "", remoteIP, args.tls, args.nbd_port)
5050

51-
nbdClient = nbdcli.client(cType)
51+
nbdClient = nbdcli.client(cType, False)
5252
logging.info("Started NBD server, PID: [%s]", proc.pid)
5353
return nbdClient.connect()
5454

man/virtnbdbackup.1

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
2-
.TH VIRTNBDBACKUP "1" "March 2025" "virtnbdbackup 2.23" "User Commands"
2+
.TH VIRTNBDBACKUP "1" "March 2025" "virtnbdbackup 2.24" "User Commands"
33
.SH NAME
44
virtnbdbackup \- backup utility for libvirt
55
.SH DESCRIPTION
@@ -8,8 +8,9 @@ usage: virtnbdbackup [\-h] \fB\-d\fR DOMAIN [\-l {copy,full,inc,diff,auto}]
88
[\-t {stream,raw}] [\-r] \fB\-o\fR OUTPUT [\-C CHECKPOINTDIR]
99
[\-\-scratchdir SCRATCHDIR] [\-S] [\-i INCLUDE] [\-x EXCLUDE]
1010
[\-f SOCKETFILE] [\-n] [\-z [COMPRESS]] [\-w WORKER]
11-
[\-F FREEZE_MOUNTPOINT] [\-e] [\-T THRESHOLD] [\-U URI]
12-
[\-\-user USER] [\-\-ssh\-user SSH_USER] [\-\-ssh\-port SSH_PORT]
11+
[\-F FREEZE_MOUNTPOINT] [\-e] [\-\-no\-sparse\-detection]
12+
[\-T THRESHOLD] [\-U URI] [\-\-user USER]
13+
[\-\-ssh\-user SSH_USER] [\-\-ssh\-port SSH_PORT]
1314
[\-\-password PASSWORD] [\-P NBD_PORT] [\-I NBD_IP] [\-\-tls]
1415
[\-\-tls\-cert TLS_CERT] [\-L] [\-\-quiet] [\-\-nocolor] [\-q]
1516
[\-s] [\-k] [\-p] [\-v] [\-V]
@@ -52,7 +53,7 @@ Backup only disk with target dev name (\fB\-i\fR vda)
5253
Exclude disk(s) with target dev name (\fB\-x\fR vda,vdb)
5354
.TP
5455
\fB\-f\fR SOCKETFILE, \fB\-\-socketfile\fR SOCKETFILE
55-
Use specified file for NBD Server socket (default: \fI\,/var/tmp/virtnbdbackup.117051\/\fP)
56+
Use specified file for NBD Server socket (default: \fI\,/var/tmp/virtnbdbackup.535505\/\fP)
5657
.TP
5758
\fB\-n\fR, \fB\-\-noprogress\fR
5859
Disable progress bar
@@ -69,6 +70,9 @@ If qemu agent available, freeze only filesystems on specified mountpoints within
6970
\fB\-e\fR, \fB\-\-strict\fR
7071
Change exit code if warnings occur during backup operation. (default: False)
7172
.TP
73+
\fB\-\-no\-sparse\-detection\fR
74+
Skip detection of sparse ranges during incremental or differential backup. (default: False)
75+
.TP
7276
\fB\-T\fR THRESHOLD, \fB\-\-threshold\fR THRESHOLD
7377
Execute backup only if threshold is reached.
7478
.SS "Remote Backup options:"

man/virtnbdmap.1

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
2-
.TH VIRTNBDMAP "1" "March 2025" "virtnbdmap 2.23" "User Commands"
2+
.TH VIRTNBDMAP "1" "March 2025" "virtnbdmap 2.24" "User Commands"
33
.SH NAME
44
virtnbdmap \- map virtnbdbackup image files to nbd devices
55
.SH DESCRIPTION

man/virtnbdrestore.1

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
2-
.TH VIRTNBDRESTORE "1" "March 2025" "virtnbdrestore 2.23" "User Commands"
2+
.TH VIRTNBDRESTORE "1" "March 2025" "virtnbdrestore 2.24" "User Commands"
33
.SH NAME
44
virtnbdrestore \- restore utility for libvirt
55
.SH DESCRIPTION
@@ -41,7 +41,7 @@ Process only disk matching target dev name. (default: None)
4141
Disable progress bar
4242
.TP
4343
\fB\-f\fR SOCKETFILE, \fB\-\-socketfile\fR SOCKETFILE
44-
Use specified file for NBD Server socket (default: \fI\,/var/tmp/virtnbdbackup.117056\/\fP)
44+
Use specified file for NBD Server socket (default: \fI\,/var/tmp/virtnbdbackup.535510\/\fP)
4545
.TP
4646
\fB\-r\fR, \fB\-\-raw\fR
4747
Copy raw images as is during restore. (default: False)

t/Makefile

+5-3
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ vm4.tests: $(bats)
1515
vm5.tests: $(bats)
1616
$(call clean)
1717
export TEST=vm5 ; ./bats-core/bin/bats tests.bats
18-
1918
fstrim.tests: $(bats)
2019
$(call clean)
2120
export TEST=fstrim ; ./bats-core/bin/bats fstrim.bats
21+
nosparsedetect.tests: $(bats)
22+
$(call clean)
23+
export TEST=nosparsedetect ; ./bats-core/bin/bats nosparsedetect.bats
2224

2325

24-
all: | $(bats) vm1.tests vm2.tests vm3.tests vm4.tests fstrim.tests
26+
all: | $(bats) vm1.tests vm2.tests vm3.tests vm4.tests fstrim.tests nosparsedetect.tests
2527

2628
clean:
2729
$(call clean)
@@ -35,7 +37,7 @@ define clean
3537
@rm -rf checkpoints
3638
@rm -f vmconfig*.xml
3739
@umount -lf /empty || true
38-
for vm in vm1 restore_vm1 vm2 vm3 vm4 vm5 fstrim; do \
40+
for vm in vm1 restore_vm1 vm2 vm3 vm4 vm5 fstrim nosparsedetect restored; do \
3941
if virsh list --all | grep $$vm; then \
4042
virsh destroy $$vm; \
4143
virsh undefine $$vm --checkpoints-metadata --nvram --managed-save; \

0 commit comments

Comments
 (0)