From c9cbe424d7fee11abde79c1c0342adc17a0eb9ae Mon Sep 17 00:00:00 2001 From: Edwin Eefting Date: Tue, 24 Sep 2024 20:29:29 +0200 Subject: [PATCH] major change: we now always filter all properties that start with "autobackup:". fixes #150 and #221 --- tests/test_zfsautobackup31.py | 29 +++++++++++++++++++++++++---- zfs_autobackup/ZfsAuto.py | 19 +++++++------------ zfs_autobackup/ZfsAutobackup.py | 4 ++++ zfs_autobackup/ZfsDataset.py | 5 +++++ 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/tests/test_zfsautobackup31.py b/tests/test_zfsautobackup31.py index 17de12f..e8650a7 100644 --- a/tests/test_zfsautobackup31.py +++ b/tests/test_zfsautobackup31.py @@ -55,23 +55,44 @@ def test_re_replication(self): shelltest("zfs create test_target1/b") with mocktime("20101111000000"): - self.assertFalse(ZfsAutobackup("test test_target1/a --no-progress --verbose --debug".split(" ")).run()) + self.assertFalse(ZfsAutobackup("test test_target1/a --no-progress --verbose --debug --allow-empty".split(" ")).run()) + + #NOTE: since v3.4 this changed. autobackup: properties are filtered. So its up to the admin to reset this property on the other side: + shelltest("zfs set autobackup:test=true test_target1/a") with mocktime("20101111000001"): - self.assertFalse(ZfsAutobackup("test test_target1/b --no-progress --verbose".split(" ")).run()) + self.assertFalse(ZfsAutobackup("test test_target1/b --no-progress --verbose --allow-empty".split(" ")).run()) r=shelltest("zfs list -H -o name -r -t snapshot test_target1") #NOTE: it wont backup test_target1/a/test_source2/fs2/sub to test_target1/b since it doesnt have the zfs_autobackup property anymore. - self.assertMultiLineEqual(r,""" + self.assertMultiLineEqual(""" +test_target1/a@test-20101111000001 +test_target1/a/test_source1@test-20101111000001 test_target1/a/test_source1/fs1@test-20101111000000 +test_target1/a/test_source1/fs1@test-20101111000001 test_target1/a/test_source1/fs1/sub@test-20101111000000 +test_target1/a/test_source1/fs1/sub@test-20101111000001 +test_target1/a/test_source2@test-20101111000001 +test_target1/a/test_source2/fs2@test-20101111000001 test_target1/a/test_source2/fs2/sub@test-20101111000000 +test_target1/a/test_source2/fs2/sub@test-20101111000001 test_target1/b/test_source1/fs1@test-20101111000000 +test_target1/b/test_source1/fs1@test-20101111000001 test_target1/b/test_source1/fs1/sub@test-20101111000000 +test_target1/b/test_source1/fs1/sub@test-20101111000001 test_target1/b/test_source2/fs2/sub@test-20101111000000 +test_target1/b/test_source2/fs2/sub@test-20101111000001 +test_target1/b/test_target1/a@test-20101111000001 +test_target1/b/test_target1/a/test_source1@test-20101111000001 test_target1/b/test_target1/a/test_source1/fs1@test-20101111000000 +test_target1/b/test_target1/a/test_source1/fs1@test-20101111000001 test_target1/b/test_target1/a/test_source1/fs1/sub@test-20101111000000 -""") +test_target1/b/test_target1/a/test_source1/fs1/sub@test-20101111000001 +test_target1/b/test_target1/a/test_source2@test-20101111000001 +test_target1/b/test_target1/a/test_source2/fs2@test-20101111000001 +test_target1/b/test_target1/a/test_source2/fs2/sub@test-20101111000000 +test_target1/b/test_target1/a/test_source2/fs2/sub@test-20101111000001 +""",r) def test_zfs_compressed(self): diff --git a/zfs_autobackup/ZfsAuto.py b/zfs_autobackup/ZfsAuto.py index c8a98fe..58ecc66 100644 --- a/zfs_autobackup/ZfsAuto.py +++ b/zfs_autobackup/ZfsAuto.py @@ -41,17 +41,14 @@ def parse_args(self, argv): # may still need to be used to explicitly exclude a backup with the 'received' source property to avoid accidental # recursive replication of a zvol that is currently being received in another session (as it will have changes). + # Follow up note: This isnt a problem anymore since v3.4, we now filter the autobackup-property by default to prevent these difficult issues. + self.exclude_paths = [] if args.ssh_source == args.ssh_target: if args.target_path: # target and source are the same, make sure to exclude target_path self.verbose("NOTE: Source and target are on the same host, excluding target-path from selection.") self.exclude_paths.append(args.target_path) - else: - if not args.exclude_received and not args.include_received: - self.verbose( - "NOTE: Source and target are on the same host, adding --exclude-received to commandline. (use --include-received to overrule)") - args.exclude_received = True if args.test: self.warning("TEST MODE - SIMULATING WITHOUT MAKING ANY CHANGES") @@ -107,10 +104,9 @@ def get_parser(self): group.add_argument('--exclude-unchanged', metavar='BYTES', default=0, type=int, help='Exclude datasets that have less than BYTES data changed since any last snapshot. (Use with proxmox HA replication)') group.add_argument('--exclude-received', action='store_true', - help='Exclude datasets that have the origin of their autobackup: property as "received". ' - 'This can avoid recursive replication between two backup partners.') - group.add_argument('--include-received', action='store_true', - help=argparse.SUPPRESS) + help='Exclude datasets that have the origin of their autobackup: property as "received".' , ) + # group.add_argument('--include-received', action='store_true', + # help=argparse.SUPPRESS) def regex_argument_type(input_line): """Parses regex arguments into re.Pattern objects""" @@ -126,9 +122,8 @@ def regex_argument_type(input_line): def print_error_sources(self): self.error( - "No source filesystems selected, please do a 'zfs set autobackup:{0}=true' on the source datasets " - "you want to select.".format( - self.args.backup_name)) + "No source filesystems selected, please do a 'zfs set {}=true' on the source datasets " + "you want to select.".format(self.property_name)) def make_target_name(self, source_dataset): """make target_name from a source_dataset""" diff --git a/zfs_autobackup/ZfsAutobackup.py b/zfs_autobackup/ZfsAutobackup.py index ff2387e..51e04c2 100644 --- a/zfs_autobackup/ZfsAutobackup.py +++ b/zfs_autobackup/ZfsAutobackup.py @@ -50,6 +50,8 @@ def parse_args(self, argv): if args.decrypt: self.warning("Properties will not be sent over for datasets that will be decrypted. (zfs bug https://github.com/openzfs/zfs/issues/16275)") + + return args def get_parser(self): @@ -93,6 +95,7 @@ def get_parser(self): group.add_argument('--set-properties', metavar='PROPERTY=VALUE,...', type=str, help='List of propererties to override when receiving filesystems. (you can still restore ' 'them with zfs inherit -S)') + group.add_argument('--rollback', action='store_true', help='Rollback changes to the latest target snapshot before starting. (normally you can ' 'prevent changes by setting the readonly property on the target_path to on)') @@ -439,6 +442,7 @@ def filter_properties_list(self): if self.args.clear_refreservation: filter_properties.append("refreservation") + return filter_properties def set_properties_list(self): diff --git a/zfs_autobackup/ZfsDataset.py b/zfs_autobackup/ZfsDataset.py index 055e885..58f6341 100644 --- a/zfs_autobackup/ZfsDataset.py +++ b/zfs_autobackup/ZfsDataset.py @@ -1188,6 +1188,11 @@ def sync_snapshots(self, target_dataset, features, show_progress, filter_propert (active_filter_properties, active_set_properties) = self.get_allowed_properties(filter_properties, set_properties) + # always filter properties that start with 'autobackup:' (https://github.com/psy0rz/zfs_autobackup/issues/221) + for property in self.properties: + if property.startswith('autobackup:'): + active_filter_properties.append(property) + # encrypt at target? if encrypt and not raw: # filter out encryption properties to let encryption on the target take place