Compare commits

...

243 Commits

Author SHA1 Message Date
Jakob Rath 114724b0a4 Replicate all dependencies of a dataset first
Assuming we want to replicate the following pool:

```
NAME            USED  AVAIL  REFER  MOUNTPOINT              ORIGIN
testpool1      1.10M  38.2M   288K  /Volumes/testpool1      -
testpool1/A     326K  38.2M   293K  /Volumes/testpool1/A    testpool1/B@b
testpool1/A/D   303K  38.2M   288K  /Volumes/testpool1/A/D  -
testpool1/B    35.5K  38.2M   292K  /Volumes/testpool1/B    testpool1/C@a
testpool1/C     306K  38.2M   290K  /Volumes/testpool1/C    -
```

Note the clone dependencies: `A -> B -> C`.

Currently, syncoid notices that `A` and `B` are clones and defers syncing them.
There are two problems:

1. Syncing `A/D` fails because we have deferred `A`.

2. The clone relation `A -> B` will not be recreated since the list of deferred datasets does not take into account clone relations between them.

This PR solves both of these problems by collecting all dependencies of a dataset and syncing them before the dataset itself.

---

One problematic case remains: if a dataset depends (transitively) on one of its own children, e.g.:

```
NAME            USED  AVAIL  REFER  MOUNTPOINT              ORIGIN
testpool1/E    58.5K  38.7M   298K  /Volumes/testpool1/E    testpool1/E/D@e
testpool1/E/D  37.5K  38.7M   296K  /Volumes/testpool1/E/D  testpool1/A@d
```

Here, the first run of syncoid will fail to sync `E/D`.
I've chosen to ignore this case for now because
1) it seems quite artificial and not like something that would occur in practice very often, and
2) a second run of syncoid will successfully sync `E/D` too (although the clone relation `E -> E/D` is lost).
2024-06-09 14:13:15 +02:00
Jim Salter a7e6c2db68
Merge pull request #920 from phreaker0/dataset-cache
[sanoid] implemented dataset cache and fix race conditions
2024-04-26 16:28:48 -04:00
Christoph Klaffl 9c0468ee45
write cache files in an atomic way to prevent race conditions 2024-04-24 00:09:40 +02:00
Christoph Klaffl 6f74c7c4b3
* improve performance (especially for monitor commands) by caching the dataset list
* list snapshots only when needed
2024-04-23 23:38:47 +02:00
Jim Salter b31ed6e325
Merge pull request #916 from 0xFelix/zstdmt
syncoid: Add zstdmt compress options
2024-04-22 12:14:40 -04:00
Jim Salter fa2c16d65a
Merge pull request #905 from phreaker0/findoid-relative-path
[findoid] support relative paths
2024-04-22 12:13:43 -04:00
Jim Salter 1207ea0062
Merge pull request #904 from phreaker0/tests-restructure
test adaptions
2024-04-22 12:13:28 -04:00
Jim Salter d800e5e17d
Merge pull request #903 from spicyFajitas/regather_snapshots--delete-target-snaps_task
fix(syncoid): regather $snaps on --delete-target-snapshots flag
2024-04-22 12:12:58 -04:00
Jim Salter 1ee6815e5e
Merge pull request #910 from phreaker0/improve-output
added missing status information about what is done and provide more details
2024-04-22 12:12:28 -04:00
0xFelix 8b7d29d5a0 syncoid: Add zstdmt compress options
Add the zstdmt-fast and zstdmt-slow compress options to allow use of
multithreading when using zstd compression.

Signed-off-by: 0xFelix <felix@matouschek.org>
2024-04-20 18:41:43 +02:00
Christoph Klaffl b4c8e4b499
Merge branch 'master' into improve-output 2024-04-18 14:30:04 +02:00
Jim Salter 45b1ce9e5d
Merge pull request #911 from phreaker0/fix-error-handling
handle error output for filtered replications
2024-04-18 08:25:30 -04:00
Christoph Klaffl 6c1e31e551
handle error output for filtered replications 2024-04-18 08:22:37 +02:00
Christoph Klaffl eb4fe8a01c
added missing status information about what is done and provide more details 2024-04-18 07:42:47 +02:00
Jim Salter fdbbe28ac7
Merge pull request #909 from phreaker0/socket-rename
rename ssh control socket to avoid problem with length limits and con…
2024-04-17 09:11:04 -04:00
Christoph Klaffl a059054ffb
rename ssh control socket to avoid problem with length limits and conflicts 2024-04-17 08:14:04 +02:00
Christoph Klaffl d7ed4bdf54
support relative paths 2024-04-05 15:24:42 +02:00
Christoph Klaffl 4e86733c1a
missed debug statement 2024-04-05 15:22:13 +02:00
Christoph Klaffl 7c8a34eceb
* proper order of tests
* timing fixes for fast NVME pools
* skip invasive tests by default
2024-04-05 15:20:28 +02:00
Adam Fulton d08b2882b7 finish rebase to master 2024-04-01 13:16:16 -05:00
Adam Fulton f89372967f fix(syncoid): regather $snaps on --delete-target-snapshots flag 2024-04-01 13:12:59 -05:00
Jim Salter 19fc237476
Update INSTALL.md 2024-02-01 15:05:08 -05:00
Jim Salter d5ce1889d6
Create SECURITY.md 2024-02-01 14:59:43 -05:00
Jim Salter 4e101bbc16
Create CONTRIBUTING.md 2024-02-01 14:52:25 -05:00
Jim Salter b420048d95
Create CODE_OF_CONDUCT.md 2024-02-01 14:45:33 -05:00
Jim Salter 5de562eb7f
Update README.md 2024-02-01 14:38:45 -05:00
Jim Salter 7940f65941
Update README.md 2024-02-01 13:58:33 -05:00
Jim Salter 6919bc3324
Update README.md 2024-02-01 13:57:02 -05:00
Jim Salter 7c225a1d7b
Merge pull request #818 from Deltik/fix/815
syncoid: Sort snapshots by `createtxg` if possible (fallback to `creation`)
2024-02-01 13:13:35 -05:00
Jim Salter acdc0938c9
Merge pull request #884 from dlangille/master
sanoid.conf: document two options for recursive
2024-01-26 14:16:44 -05:00
Jim Salter e0bd202c41
Merge pull request #856 from Pajkastare/master
Fixes jimsalterjrs/sanoid#851
2024-01-26 14:16:14 -05:00
Christoph Klaffl 6667f02d35
Update sanoid.conf 2024-01-25 21:13:00 +01:00
Christoph Klaffl 7dae0e5a9b
Merge branch 'master' into master 2024-01-25 21:12:11 +01:00
pajkastare 01053e6cce Removed unnecessary comment, no code change 2024-01-24 13:51:24 +01:00
pajkastare a8c15c977a Fixes jimsalterjrs/sanoid#851, updated based on review in discussion thread 2024-01-24 13:32:22 +01:00
Dan Langille 9ed32d177d sanoid.conf: document two options for recursive
zfs and yes are the options, one uses zfs, the other sanoid code
2024-01-15 09:56:47 -05:00
Jim Salter a5fa5e7bad
Merge pull request #843 from mjeanson/master
Fix typos in syncoid documentation
2024-01-13 21:32:08 -05:00
Jim Salter d60ee1ffc7
Merge pull request #855 from EchterAgo/debian_depends_openzfs_native_deb
debian: add openzfs-zfsutils as an alternative to zfsutils-linux
2024-01-13 21:31:50 -05:00
Jim Salter c30d485383
Merge pull request #872 from Rantherhin/zfs-get
fix zfs-get for "--preserve-properties" and tests
2024-01-13 21:30:56 -05:00
Jim Salter c02defd80b
Merge pull request #841 from thecatontheflat/patch-1
Update INSTALL.md
2024-01-13 21:30:38 -05:00
Christoph Klaffl 790ea544ff
Merge branch 'master' into zfs-get 2024-01-13 23:27:38 +01:00
Jim Salter b100ba43ac
Merge pull request #859 from jan-krieg/master
fix "creation"/"guid" regex detection
2024-01-13 16:33:01 -05:00
Christoph Klaffl 0361faac76
Merge branch 'master' into master 2024-01-13 21:56:31 +01:00
Jim Salter d01eef7555
Merge pull request #846 from jiawen/master-1
Fix tiny typo in README.md
2024-01-13 15:47:19 -05:00
Jim Salter 54c2dacd20
Merge pull request #881 from phreaker0/force-delete-skip-root
prevent destroying of root dataset which leads to infinite loop
2024-01-13 15:44:18 -05:00
Jim Salter 4e8b881da7
Merge pull request #882 from phreaker0/preserve-properties-handle-special-symbols
escape property key and value pair in case of property preservation
2024-01-13 15:41:57 -05:00
Jim Salter af732daccf
Merge pull request #883 from phreaker0/update-send-recv-options
update possible zfs send options
2024-01-13 15:41:41 -05:00
Christoph Klaffl becddb854f
Merge branch 'master' into preserve-properties-handle-special-symbols 2024-01-13 21:34:45 +01:00
Christoph Klaffl 85e7fca30e
Merge branch 'master' into force-delete-skip-root 2024-01-13 21:29:40 +01:00
Christoph Klaffl ca6e60b920
Merge branch 'master' into update-send-recv-options 2024-01-13 21:22:51 +01:00
Jim Salter 680bf23412
Merge pull request #699 from mr-vinn/filter-snaps
Add --include-snaps and --exclude-snaps options to syncoid
2024-01-13 14:45:03 -05:00
Christoph Klaffl 8ce1ea4dc8
fixed refactoring regression 2024-01-13 19:49:20 +01:00
Christoph Klaffl e9eb05e840
Merge branch 'master' into filter-snaps 2024-01-13 19:40:28 +01:00
Christoph Klaffl 6761004939
update possible zfs send options 2024-01-11 21:02:04 +01:00
Christoph Klaffl 4369576ac4
escape property key and value pair in case of property preservation 2024-01-09 20:40:33 +01:00
Christoph Klaffl 48d89c785e
prevent destroying of root dataset which leads to infinite loop because it can't be destroyed 2024-01-09 19:53:03 +01:00
Justin Wolf dbbaac8ac3 modify zfs-get argument order for portability 2023-12-10 21:16:42 -06:00
Jan Krieg 605b7bac1c
fix "creation"/"guid" regex detection 2023-10-29 17:46:28 +01:00
pajkastare a5a6fc0f58 Fixes jimsalterjrs/sanoid#851 2023-10-23 21:43:46 +02:00
Axel Gembe 07b6d6344c
debian: add openzfs-zfsutils as an alternative to zfsutils-linux
The package produced by ZFS 2.2.0 `make native-deb-utils` is called
`openzfs-zfsutils`.
2023-10-15 14:07:09 +07:00
Jiawen (Kevin) Chen 18ccb7df35
Fix tiny typo in README.md 2023-08-14 22:52:16 -07:00
Michael Jeanson 6b874a7e3c Fix typos in syncoid documentation
Signed-off-by: Michael Jeanson <mjeanson@efficios.com>
2023-08-03 16:17:51 -04:00
Vitalii Zurian a881d22c85
Update INSTALL.md 2023-08-01 10:05:46 +02:00
Jim Salter 61000c9da2
Merge pull request #837 from phreaker0/trim-config-values
trim config values
2023-07-25 17:29:06 -04:00
Christoph Klaffl 7ab0ac0cf6
trim config values 2023-07-25 22:56:25 +02:00
Jim Salter 91a960ffc5
Merge pull request #832 from phreaker0/prepare-2.2.0
prepare 2.2.0
2023-07-18 12:29:53 -04:00
Christoph Klaffl 538416879d
prepare 2.2.0 2023-07-18 18:09:51 +02:00
Christoph Klaffl f3d4d309b5
implemented flag for preserving properties without the zfs -p flag 2023-07-18 08:38:40 +02:00
Nick Liu a904ba02f3
enh(run-tests.sh): Sort tests with "general numeric sort"
The sort before tended to be alphabetical, which put test
`8_force_delete_snapshot` after `815_sync_out-of-order_snapshots`, but
`8` should come before `815`.

Before:

```
root@demo:~/sanoid/tests/syncoid# ./run-tests.sh
Running test 1_bookmark_replication_intermediate ... [PASS]
Running test 2_bookmark_replication_no_intermediate ... [PASS]
Running test 3_force_delete ... [PASS]
Running test 4_bookmark_replication_edge_case ... [PASS]
Running test 5_reset_resume_state ... mbuffer: error: outputThread: error writing to <stdout> at offset 0x90000: Broken pipe
mbuffer: warning: error during output to <stdout>: Broken pipe
[PASS]
Running test 6_reset_resume_state2 ... [PASS]
Running test 7_preserve_recordsize ... [PASS]
Running test 815_sync_out-of-order_snapshots ... [PASS]
Running test 8_force_delete_snapshot ... [PASS]
```

After:

```
root@demo:~/sanoid/tests/syncoid# ./run-tests.sh
Running test 1_bookmark_replication_intermediate ... [PASS]
Running test 2_bookmark_replication_no_intermediate ... [PASS]
Running test 3_force_delete ... [PASS]
Running test 4_bookmark_replication_edge_case ... [PASS]
Running test 5_reset_resume_state ... mbuffer: error: outputThread: error writing to <stdout> at offset 0xf0000: Broken pipe
mbuffer: warning: error during output to <stdout>: Broken pipe
[PASS]
Running test 6_reset_resume_state2 ... [PASS]
Running test 7_preserve_recordsize ... [PASS]
Running test 8_force_delete_snapshot ... [PASS]
Running test 815_sync_out-of-order_snapshots ... [PASS]
```
2023-04-28 01:00:03 -05:00
Jim Salter 55c5e0ee09
Merge pull request #513 from phreaker0/direct-connection
implemented option for direct connection via socat and busybox nc
2023-04-27 14:26:11 -04:00
Jim Salter c69381e5b9
Merge pull request #819 from phreaker0/fixed-default-options
fix default behaviour if run without providing arguments
2023-04-27 14:22:47 -04:00
Christoph Klaffl 1e3874aea5
Merge branch 'master' into direct-connection 2023-04-27 09:16:51 +02:00
Jim Salter 3bde229a61
Merge pull request #622 from rbike/syncoid-hold
syncoid hold
2023-04-26 14:02:15 -04:00
Christoph Klaffl 01ace52642
Merge branch 'master' into syncoid-hold 2023-04-26 19:59:20 +02:00
Jim Salter f8ff5ab0c2
Merge pull request #809 from phreaker0/handle-same-second-bookmarks
support bookmarks which are taken in the same second
2023-04-26 13:49:37 -04:00
Christoph Klaffl cc495183af
fix default behaviour if run without providing arguments 2023-04-26 19:47:49 +02:00
Christoph Klaffl 2143f629d5
Merge remote-tracking branch 'upstream/master' into handle-same-second-bookmarks 2023-04-26 19:40:24 +02:00
Jim Salter e6585e7ee5
Merge pull request #746 from mavhc/master
Removed unneeded 2>/dev/null from mbufferoptions
2023-04-26 13:00:03 -04:00
Jim Salter 6804826cf4
Merge pull request #808 from phreaker0/fix-tests
fixes some tests which fail on fast storage
2023-04-26 12:59:27 -04:00
Jim Salter ed1c542850
Merge pull request #523 from mat813/snapshot-removal
Add target snapshot deletion.
2023-04-26 12:58:29 -04:00
Nick Liu b37092f376
test(syncoid): Add test to verify out-of-order snapshot sync
See https://github.com/jimsalterjrs/sanoid/issues/815 for the original
test.
2023-04-25 17:35:45 -05:00
Nick Liu ab361017e7
feat(syncoid): Match snapshots to bookmarks by `createtxg` if possible
This is a continuation of a previous commit to sort snapshots by
`createtxg` if possible.  Now, we have to match the behavior when
selecting an appropriate snapshot based on the transaction group of the
relevant bookmark in `syncdataset()`.

Supersedes: https://github.com/jimsalterjrs/sanoid/pull/667
2023-04-25 17:07:32 -05:00
Nick Liu 8907e0cb2f
feat(syncoid): Sort snapshots by `createtxg` if possible
It is possible for `creation` of a subsequent snapshot to be in the past
compared to the current snapshot due to system clock discrepancies,
which leads to earlier snapshots not being replicated in the initial
syncoid sync.

Also, `syncoid --no-sync-snap` might not pick up the most recently taken
snapshot if the clock moved backwards before taking that snapshot.

Sorting snapshots by the `createtxg` value is reliable and documented
in `man 8 zfsprops` as the proper way to order snapshots, but it was not
available in ZFS versions before 0.7.  To maintain backwards
compatibility, the sorting falls back to sorting by the `creation`
property, which was the old behavior.

Fixes: https://github.com/jimsalterjrs/sanoid/issues/815
2023-04-28 00:43:47 -05:00
Nick Liu 8fabaae5b8
feat(syncoid): Add "createtxg" property to `getsnaps`
The `getsnaps` subroutine now retrieves the "createtxg" property of the
snapshot.

This is necessary to support the fix for
https://github.com/jimsalterjrs/sanoid/issues/815 (Syncoid: Data loss
because getoldestsnapshot() might not choose the first snapshot).
2023-04-25 14:01:54 -05:00
Nick Liu e301b5b153
refactor(syncoid): Simplify getsnaps to parse a hash rather than lines
* The part that was "a little obnoxious" has been rewritten to extract
  the desired properties in a single loop after importing each line into
  a hash rather than processing line by line with a state tracking flag.
* The `getsnapsfallback` subroutine had duplicated logic that has been
  absorbed into `getsnaps` with a recursion argument to enable the
  fallback mode.
2023-04-25 13:58:40 -05:00
Mathieu Arnold 2f706a4ae1
Batch snapshot deletion.
This is to prevent a problem with a large amount of snapshots which
exceed the allowed shell command length.
2023-04-08 10:09:11 +02:00
Mathieu Arnold f711e6bf28
Add a note about when snapshot deletion is done. 2023-04-08 09:45:01 +02:00
Mathieu Arnold ecd1400539
Handle output/errors of those zfs destroy commands.
If there was an obsolete remote syncoid_hostname_* snapshot that did not
get removed at the correct time, for some reason, like, maybe, network
problems, it would have been cleaned up in pruneoldsyncsnaps just before
this code, and we would get a strange error message saying:

    could not find any snapshots to destroy; check snapshot names.

Also, when using --quiet, do not output anything, as failing to remove
an obsolete snapshot is not really a big problem.
2023-04-08 09:45:01 +02:00
Mathieu Arnold 63dd819ec5
Rename option. 2023-04-08 09:45:00 +02:00
Mathieu Arnold 2d89434ac3
Add target snapshot deletion. 2023-04-08 09:42:58 +02:00
Christoph Klaffl 9e04911945
Merge branch 'master' into direct-connection 2023-03-24 09:16:13 +01:00
Christoph Klaffl 91d96a4c87
support bookmarks which are taken in the same second 2023-03-24 09:00:07 +01:00
Christoph Klaffl a7f9f08f47
fixes some tests which fail on fast storage 2023-03-21 16:44:38 +01:00
Christoph Klaffl 0b27059133
Merge remote-tracking branch 'upstream/master' into filter-snaps 2023-03-21 16:35:20 +01:00
Christoph Klaffl f3152d6f6c
Merge remote-tracking branch 'upstream/master' into syncoid-hold 2023-03-21 15:44:04 +01:00
Jim Salter 86fffca306
Merge pull request #807 from phreaker0/pr-merger
PR Merger
2023-03-21 02:04:45 -04:00
Christoph Klaffl e5b1d0fd4f
Merge branch 'pr796' into pr-merger 2023-03-20 22:31:40 +01:00
Christoph Klaffl b3d32d9394
Merge branch 'pr788' into pr-merger 2023-03-20 22:31:19 +01:00
Christoph Klaffl 50ff9b7d31
Merge branch 'pr775' into pr-merger 2023-03-20 22:29:17 +01:00
Christoph Klaffl fda6b52403
Merge branch 'pr765' into pr-merger 2023-03-20 22:28:15 +01:00
Christoph Klaffl 3b0f28184a
Merge branch 'pr701' into pr-merger 2023-03-20 22:13:34 +01:00
Christoph Klaffl e6f40483eb
Merge branch 'pr690' into pr-merger 2023-03-20 22:07:09 +01:00
Christoph Klaffl 52c30bfad7
Merge branch 'pr679' into pr-merger 2023-03-20 22:06:21 +01:00
Christoph Klaffl d5dba5e3ed
Merge branch 'pr670' into pr-merger 2023-03-20 22:01:43 +01:00
Christoph Klaffl dc0f13268b
Merge branch 'pr657' into pr-merger 2023-03-20 21:55:44 +01:00
Christoph Klaffl d55b29ef04
Merge branch 'pr643' into pr-merger 2023-03-20 21:52:24 +01:00
Christoph Klaffl de013fe13f
Merge branch 'pr638' into pr-merger 2023-03-20 21:50:46 +01:00
Christoph Klaffl 561220a5f9
Merge branch 'pr637' into pr-merger 2023-03-20 21:50:26 +01:00
Christoph Klaffl f942341f8d
Merge branch 'pr636' into pr-merger 2023-03-20 21:49:00 +01:00
Christoph Klaffl f2982d9313
Merge branch 'pr592' into pr-merger 2023-03-20 21:33:20 +01:00
Christoph Klaffl 42ed026837
Merge branch 'pr538' into pr-merger 2023-03-20 21:20:26 +01:00
Christoph Klaffl 9e87e8c95a
Merge branch 'pr769' into pr-merger 2023-03-20 21:14:43 +01:00
Christoph Klaffl 487fba24e5
Merge branch 'pr768' into pr-merger 2023-03-20 21:14:34 +01:00
Christoph Klaffl b488611ae7
Merge branch 'pr767' into pr-merger 2023-03-20 21:14:29 +01:00
Christoph Klaffl c2f5367d3d
Merge branch 'pr766' into pr-merger 2023-03-20 21:14:25 +01:00
Christoph Klaffl 9625a79e25
Merge branch 'pr608' into pr-merger 2023-03-20 21:14:13 +01:00
Christoph Klaffl 6deae74b7d
Merge branch 'pr606' into pr-merger 2023-03-20 21:14:07 +01:00
Christoph Klaffl 59544de134
Merge branch 'pr605' into pr-merger 2023-03-20 21:14:02 +01:00
Christoph Klaffl ea59c0f598
Merge branch 'pr506' into pr-merger 2023-03-20 21:13:52 +01:00
dodexahedron 38f2d625a8
Clarified that compression is on the wire 2023-02-02 19:32:08 -07:00
Josh Soref ae28c10982 spelling: want
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-01-01 14:43:35 -05:00
Josh Soref 2333f1142d spelling: suppress
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-01-01 14:43:35 -05:00
Josh Soref 0fcaab54f6 spelling: snapshotted
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-01-01 14:43:35 -05:00
Josh Soref 3ffa57c72f spelling: resumable
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-01-01 14:43:35 -05:00
Josh Soref 7a8b0adb6d spelling: overridden
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-01-01 14:43:35 -05:00
Josh Soref 93f2b8863e spelling: necessary
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-01-01 14:43:35 -05:00
Josh Soref 9421892402 spelling: naming
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-01-01 14:43:35 -05:00
Josh Soref a666424742 spelling: mbytes
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-01-01 14:43:35 -05:00
Josh Soref c43048598a spelling: github
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-01-01 14:43:35 -05:00
Josh Soref 12e962a133 spelling: errors
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-01-01 14:43:35 -05:00
Josh Soref c37f412797 spelling: errlevel
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-01-01 14:35:55 -05:00
Josh Soref cad1215501 spelling: debugging
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-01-01 14:30:57 -05:00
Josh Soref 7d24d97045 spelling: available
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-01-01 14:30:57 -05:00
Josh Soref 9f898439e0 spelling: a lot
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-01-01 14:30:57 -05:00
Vinnie Okada 0c577fc735 Deprecate the --exclude option
Add a new option, --exclude-datasets, to replace --exclude. This makes
the naming more consistent now that there are options to filter both
snapshots and datasets.

Also add more information to the README about the distinction between
--exclude-datasets and --(in|ex)clude-snaps.
2022-12-24 12:59:59 -07:00
Vinnie Okada 14ed85163a Filter snapshots in getsnapsfallback() 2022-12-24 12:55:17 -07:00
Vinnie Okada 8e867c6f14 Add new syncoid tests
Test the new --include-snaps and --exclude-snaps options for syncoid.
2022-12-24 12:55:15 -07:00
Vinnie Okada 3a1b1b006f Add new syncoid options to the README
Update the README with the new --include-snaps and --exclude-snaps
 syncoid options.
2022-12-24 12:55:13 -07:00
Vinnie Okada 9a067729a9 Implement include-snaps and exclude-snaps
Add --include-snaps and --exclude-snaps options to filter the snapshots
that syncoid uses.
2022-12-24 12:55:08 -07:00
Vinnie Okada 603c286b50 Don't iterate over snaps twice
Process snapshots in one pass rather than looping separately for both
guid and create time.
2022-12-24 12:52:06 -07:00
Vinnie Okada 09b42d6ade Refactor system calls
Build the zfs send and receive commands in a new subroutine, and
implement other subroutines that can be called instead of building a zfs
command and running it with system();
2022-12-24 12:51:58 -07:00
Vinnie Okada c4e7028022 Refactor terminal output
Replace `print` and `warn` statements with a logging function.
2022-12-24 12:48:54 -07:00
joelishness 5d469c4bb2
Update README.md
Fixed some typos
2022-10-03 21:36:26 -07:00
Christoph Klaffl 0808575874
syncoid should exit with an error if the specified src dataset doesn't exist (and only skip child datasets which vanished) 2022-09-13 17:13:01 +02:00
Christoph Klaffl a5d7896c0f
improve accuracy of zfs receive check 2022-09-13 00:16:49 +02:00
Christoph Klaffl 4f150ec6ec
fix snapshot listing fallback 2022-09-13 00:02:03 +02:00
Christoph Klaffl fa67fbe845
bring back no-rollback option 2022-09-12 23:44:52 +02:00
Daryl Bennett c408bddab4 Include syncoid remote user requirment
Add requirement for syncoid w/ remote hosts to be able to sudo without a password.
Recommends a least privilege configuration option for enabling `NOPASSWD`
2022-09-05 19:45:10 -07:00
Mark Scholes 59c59e2b00 Removed unneeded 2>/dev/null from mbufferoptions 2022-05-19 11:54:16 +01:00
Endre Szabo 21931bd8a6
Merge branch 'jimsalterjrs:master' into ssh_config 2022-04-25 09:05:58 +02:00
Jim Salter ef601eb96b Merge branch 'master' of https://github.com/jimsalterjrs/sanoid 2022-04-08 21:34:58 +00:00
Jim Salter ee8c884460 put local resume token support back in 2022-04-08 21:34:01 +00:00
Jim Salter b0a71f763a
Merge pull request #734 from Gryd3/patch-1
append a rand int to the socket name.
2022-04-08 17:18:23 -04:00
Jim Salter ef2b4f887a no more rolling targets back, receive -F only 2022-04-08 21:09:51 +00:00
Jim Salter ec7858c1c7 don't use resume tokens for purely local replication 2022-04-08 20:57:53 +00:00
Gryd3 b1c2684c63
append a rand int to the socket name.
Running more than one 'syncoid' at the same time to the same host resulted in two jobs referencing the same socket.
Often results in "already exists, disabling multiplexing" but has in more than one occasion failed with the following:

ControlSocket /tmp/syncoid-zfsbackup-zfsbackup@10.0.0.1-1649107066 already exists, disabling multiplexing
lzop: Inappropriate ioctl for device: <stdin>
CRITICAL ERROR: ssh     -S /tmp/syncoid-zfsbackup-zfsbackup@10.0.0.1-1649107066 zfsbackup@10.0.0.1 ' zfs send  -I '"'"'pool/office'"'"'@'"'"'autosnap_2022-04-04_21:00:00_frequently'"'"' '"'"'pool/office'"'"'@'"'"'autosnap_2022-04-04_21:15:00_frequently'"'"' | lzop  | mbuffer -R 5m -q -s 128k -m 16M 2>/dev/null' | mbuffer  -q -s 128k -m 16M 2>/dev/null | lzop -dfc | pv -s 18356312 |  zfs receive  -s -F 'zfs-pool/vault/office' 2>&1 failed: 256 at /usr/sbin/syncoid line 786.

Sample use-case: 
Using Monit, Cron, or some other scheduler to trigger more than syncoid to the same host to sync two datasets.
Stagger the sync so that no two jobs get started at the same time, or add some form of randomization to the socket name so that two jobs may start at the same time.
2022-04-04 15:00:31 -07:00
Topslakr 1fcf348001
Update Install.md for Centos 8
Updated the install language, and commands, to better server Centos 8 and it's derivatives. Instead of using CPAN, enabling the 'PowerTools' repo will make those modules available for installation and kept current with normal OS updates.

Updated command to enable _and_ start sanoid.timer, instead of using two commands.
2021-12-06 13:57:18 -05:00
Juergen Heiles 8cf0144c02
Merge branch 'jimsalterjrs:master' into syncoid-hold 2021-11-13 21:09:48 +01:00
Ryan David Sheasby 864ab7f95b
Update debian installation instructions
Previous instructions couldn't actually be copied and pasted. This will update the instructions to work correctly when copied as-is
2021-11-02 11:36:13 +02:00
Lilis Iskandar 523d500609
Update README.md
Signed-off-by: Lilis Iskandar <lilis@veand.co>
2021-09-29 18:19:26 +08:00
Jim Salter d863c35cac
Merge pull request #676 from JavaScriptDude/patch-1
Add Link to Wiki Detailing sanoid.conf Settings
2021-09-23 12:42:24 -04:00
JavaScriptDude 265d4a009e
Add Link to Wiki Detailing sanoid.conf Settings 2021-09-22 18:17:15 -04:00
Daryl Bennett a9ece1c7bf
Include syncoid remote user requirment
Add requirement for syncoid w/ remote hosts to be able to sudo without a password.
Recommends a least privilege configuration option for enabling `NOPASSWD`
2021-08-23 20:22:35 -04:00
Gerhard Roethlin c125835e44 MacOS Install Information
Installation information for running sanoid on MacOS via launchd.
A launchd example plist file is provided in the same way equivalent
files are created on other platforms.
2021-07-23 17:39:28 +02:00
Alex Haydock e0862dff52
Improve documentation for RHEL-family distros 2021-05-02 15:08:28 +01:00
Jim Salter 9c0a0e7822
Update FREEBSD.readme 2021-04-17 12:50:16 -04:00
Andrew Berry c2f3f5b6b1
Fix missing t on target 2021-04-15 07:05:03 -04:00
Gordon Schulz 3954008697
Fix bogus dates in %changelog to satisfy rpmlint
- 11/24/2020 -> Tuesday
- 12/04/2018 -> Tuesday

Prior to changes:

```
$ rpmlint sanoid.spec
sanoid.spec: E: specfile-error warning: bogus date in %changelog: Wed Nov 24 2020 Christoph Klaffl <christoph@phreaker.eu> - 2.1.0
sanoid.spec: E: specfile-error warning: bogus date in %changelog: Wed Dec 04 2018 Christoph Klaffl <christoph@phreaker.eu> - 2.0.0
0 packages and 1 specfiles checked; 2 errors, 0 warnings.
```

After changes:

```
$ rpmlint sanoid.spec
0 packages and 1 specfiles checked; 0 errors, 0 warnings.
```
2021-04-10 12:59:31 +02:00
git@end.re c151703837 Implementing support for ssh_config(5) files 2021-04-08 15:01:45 +02:00
Jim Salter 9564454d74
Merge pull request #601 from phreaker0/prepare-2.1.0
prepare 2.1.0
2021-04-01 11:41:44 -04:00
rbike 21eee41fdb Final revision
comments from @phreaker0 implemented
2021-04-01 09:23:48 +02:00
rbike 2858fbe907 Docs update + error handling
updated in-program docs and readme,
check if matchingsnap exists before releasing hold
2021-03-03 13:49:13 +01:00
rbike 3bae6baf3d syncoid hold
Added hold feature to syncoid.
Parameter "--use-hold" sets hold for newsyncsmap and removes hold from matchingsnap both on source and target. Hold name is "syncoid" + identiifer + host name
2021-03-02 16:03:16 +01:00
Christoph Klaffl 48eefd21e8
don't try to parse stats for spares which are in use as there are none 2020-12-14 20:57:34 +01:00
Christoph Klaffl 316b01e9c2
fix duplicate key definitions by only using the first occurence and printing a warning 2020-12-09 09:46:54 +01:00
Christoph Klaffl 309c0866fa
implemented removal of conflicting snapshots with force-delete option 2020-12-07 22:40:28 +01:00
Christoph Klaffl 3aac31a8a1
removed old rhel version 2020-11-24 11:52:45 +01:00
Christoph Klaffl 77d0135a60
prepare v2.1.0 release 2020-11-24 11:52:20 +01:00
Christoph Klaffl eaa8eb2368
Merge pull request #4 from jim-perkins/patch-3
typo  'target -bwlimit'
2020-11-03 18:46:15 +01:00
Jim Salter 50cf9c9cc5
Merge pull request #437 from phreaker0/preserve-recordsize
preserve recordsize
2020-11-03 12:19:07 -05:00
Christoph Klaffl 2da9ba2095
only preserve recordsize for datasets of type "filesystem" 2020-11-03 18:05:13 +01:00
Christoph Klaffl bdd0dfb733
Merge branch 'master' into preserve-recordsize 2020-11-03 17:50:50 +01:00
Jim Salter a4cde57d29
Merge pull request #497 from phreaker0/scripts-snapshot-grouping
Scripts snapshot grouping
2020-11-01 17:47:11 -05:00
Jim Salter 59e8949efe
Merge pull request #498 from phreaker0/syncoid-dst-fix
append timezone offset to the syncoid snapshot name to fix DST collis…
2020-11-01 17:46:32 -05:00
Jim Salter 6041f0982b
Merge pull request #399 from phreaker0/path2
don't use hardcoded paths
2020-11-01 17:28:20 -05:00
Jim Salter 2de05798f4
Merge pull request #509 from phreaker0/docs-busybox
added note for busybox based distributions
2020-11-01 17:24:39 -05:00
Jim Salter 14e6c62d0a
Merge pull request #508 from phreaker0/check-arguments
check for an invalid argument combination
2020-11-01 17:24:29 -05:00
Jim Salter 42ea8dffce
Merge pull request #507 from phreaker0/fix-busy-detection
fix iszfsbusy check for similar dataset names
2020-11-01 17:24:20 -05:00
Jim Salter 25823b1006
Merge pull request #505 from phreaker0/docs-defaults-note
added note about the default options if some interval types aren't ...
2020-11-01 17:24:11 -05:00
Jim Salter bb19048501
Merge pull request #504 from phreaker0/docs-capacity-overiding
added note about capacity limit overriding
2020-11-01 17:24:02 -05:00
Jim Salter 318f7993ef
Merge pull request #502 from phreaker0/docs-fix-argument
fix typo in argument description
2020-11-01 17:23:49 -05:00
Jim Salter 2acb3e3fea
Merge pull request #501 from phreaker0/docs-centos
extended install instructions for centos if perl dependencies can't b…
2020-11-01 17:23:33 -05:00
Jim Salter 006f897d74
Merge pull request #500 from phreaker0/docs-cron-example
added cron setup example
2020-11-01 17:23:06 -05:00
Jim Salter 345e7e58b5
Merge pull request #374 from aerusso/pulls/syncoid-dont-require-user
Do not require user to be specified for syncoid
2020-11-01 17:22:57 -05:00
Jim Salter 58263cc8aa
Merge pull request #503 from phreaker0/docs-atomic-option
document the atomic way to take snapshots recursively
2020-11-01 17:20:57 -05:00
Jim Salter f187e072b8
Merge pull request #529 from phreaker0/root-resume-check
use sudo if neccessary for checking pool capabilities regarding resum…
2020-11-01 17:19:50 -05:00
Jim Salter c21b72a376
Merge pull request #510 from phreaker0/reset-resume-2
catch another case in which the resume state isn't availabe anymore
2020-11-01 17:19:22 -05:00
Jim Salter 0ac10750a7
Merge pull request #536 from phreaker0/keep-sync-snap
implemented option for keeping sync snaps
2020-11-01 17:19:00 -05:00
Jim Salter d526d9167d
Merge pull request #580 from kr4z33/patch-1
Update INSTALL.md
2020-11-01 17:18:36 -05:00
Jim Salter e6fdc196f0
Merge pull request #576 from jim-perkins/patch-1
Update sanoid.conf
2020-11-01 17:18:15 -05:00
Antonio Russo f397687968
Do not require user to be specified for syncoid
Extends syncoid remote capabilities to match that of ssh as closely as
possible: allow a remote dataset to be specified without a username.

 - Detect if a remote reference is possible by looking for a : before
   any / characters.
 - Check if there are any pool names that might conflict with this
   name. E.g., 'weird:symbol/ds' might refer to the pool "symbol" on
   host "weird", and dataset ds. OR it might refer to the local pool
   "weird:symbol" and dataset ds.
 - Prefer local pools, matching existing behavior. No preexisting
   functioning configurations will break.
 - The name of the control socket is changed slightly.
 - A bug in the handling of remote datasets with colons in the name
   is addressed.

Signed-off-by: Antonio Russo <aerusso@aerusso.net>
2020-11-01 10:55:50 -07:00
Leo Winter d7bf12623d
Add git to apt install on Debian 2020-10-06 13:48:34 +00:00
kr4z33 b31a7ba7d8
Update INSTALL.md
"build-essential" was required for me to complete the Debian/Ubuntu instructions.
2020-08-17 19:16:26 -10:00
Jim Perkins 5092ff1000
typo 'target -bwlimit'
Use of uninitialized value in concatenation (.) or string at ./syncoid line 1317.
just an asuumption on my part considering the rest of the variables with same name dont have the space in that location
2020-08-05 11:37:48 -05:00
Jim Perkins 2c3de61b2b
Update sanoid.conf
keeps system from running sanoid on non existent datasets
2020-08-03 02:58:06 -05:00
James Bunton 6bc210deb3 fix stream sync from bookmark
syncoid fails in the following case:
- performing a stream sync (default)
- there are no matching snapshots between source and target
- there is a bookmark on the source matching a snapshot on the target
- there is an unrelated snapshot on the source older than this bookmark

This happens because the units for snapshots and bookmarks were
different. syncoid tells `zfs send` to generate an incremental stream
starting from the bookmark and going to the unrelated older snapshot.
ZFS refuses to do this because it doesn't make sense.

This fix is a bit of hack. Ideally we should share the counter code
between getsnaps and getbookmarks.
2020-05-03 23:45:29 +10:00
Christoph Klaffl 9ab57e9bb4
added documentation regarding --keep-sync-snap option 2020-05-01 17:26:54 +02:00
Christoph Klaffl d77cfdf4c7
implemented option for keeping sync snaps 2020-05-01 17:20:07 +02:00
Christoph Klaffl 0e807386c4
use sudo if neccessary for checking pool capabilities regarding resumeable send 2020-04-20 19:20:28 +02:00
Christoph Klaffl e1a6507455
direct connection will default to busybox nc again but can be switched to mbuffer 2020-04-02 21:37:06 +02:00
Christoph Klaffl fcae918ffa
updated readme regarding busybox nc -> mbuffer switch 2020-03-31 09:40:36 +02:00
Christoph Klaffl 807f6fa1bb
check if socat and mbuffer is available for insecure direct connection 2020-03-31 09:27:17 +02:00
Christoph Klaffl f0dfd46c9f
use mbuffer tcp listen socket instead of busybox nc 2020-03-30 20:15:09 +02:00
Christoph Klaffl 2e6abddb5e
fixed uninitialized value warning 2020-03-30 19:01:54 +02:00
Christoph Klaffl 84bc3164f0
implemented option for direct connection via socat and busybox nc 2020-02-21 20:34:28 +01:00
Christoph Klaffl adc99d0349
fixed minor warning 2020-02-17 17:43:23 +01:00
Christoph Klaffl b808a74e2e
catch another case were the resume state isn't availabe anymore 2020-02-14 18:38:38 +01:00
Christoph Klaffl 909d3f3765
added note for busybox based distributions 2020-02-14 09:37:38 +01:00
Christoph Klaffl 4ebdc9d173
check for an invalid argument combination 2020-02-13 18:31:56 +01:00
Christoph Klaffl b09d2b66fc
typo 2020-02-13 09:26:15 +01:00
Christoph Klaffl 13ba2b43fa
fix iszfsbusy check for similar dataset names 2020-02-13 01:37:54 +01:00
Christoph Klaffl 44f7cae115
implemented pre pruning script hook 2020-02-13 01:01:09 +01:00
Christoph Klaffl eabe3aa791
added note about the default options if some interval types aren't sepcified 2020-02-12 21:31:21 +01:00
Christoph Klaffl cf7ab69e69
added note about capacity limit overriding 2020-02-12 21:14:08 +01:00
Christoph Klaffl dd95496cd0
document the atomic way recursively take snapshots 2020-02-12 20:40:15 +01:00
Christoph Klaffl d1a161ca72
fix typo in argument description 2020-02-12 20:30:52 +01:00
Christoph Klaffl 3a7f468939
extended install instructions for centos if perl dependencies can't be found 2020-02-12 20:20:46 +01:00
Christoph Klaffl a3f242d0d4
added cron setup example 2020-02-12 20:10:28 +01:00
Christoph Klaffl 1bad3cd25b
append timezone offset to the syncoid snapshot name to fix DST collisions 2020-02-12 18:24:08 +01:00
Christoph Klaffl 0b582f6200
group snapshot creation together so pre/post scripts are only run once per dataset and prepare for future atomic grouping feature 2020-02-12 17:48:42 +01:00
Christoph Klaffl 34d6d44fa8
Merge branch 'master' into pr447 2020-02-11 17:45:54 +01:00
Christoph Klaffl 987dcc4885
Merge branch 'master' into path2 2020-01-17 10:34:23 +01:00
Christoph Klaffl d0ef3ec2d5
Merge branch 'master' into preserve-recordsize 2020-01-16 21:23:07 +01:00
tiedotguy 9e429de59d Fix typo in recursive 2019-11-03 09:34:25 +11:00
tiedotguy e01dceaee3 Remove unnecessary temporary 2019-11-02 22:35:58 +11:00
tiedotguy d51c8ab2c8 Document all the new script information passed 2019-11-02 12:51:59 +11:00
tiedotguy 46a640859f Collect and pass additional information to script execution
This will collect information about what snapshots are being taken in
a single batch (multiple snapshot types taken on a single dataset at
the same time) and pass it to pre/post scripts.  It also passes what
what type of script (pre, post, prune), and what type of snapshot is
being taken.
2019-11-02 12:42:20 +11:00
tiedotguy 6252cbf514 Restructure to pass hashes around in take_snapshots
This commit makes it easier to pass structured data between the loop
which decides what to snapshot, and the loop performing the actual
snapshot.
2019-11-02 12:42:20 +11:00
Christoph Klaffl e2532cb307
copy paste error 2019-09-25 18:36:57 +02:00
Christoph Klaffl 107545ce00
added test for preserving recordsizes 2019-09-25 18:34:26 +02:00
Christoph Klaffl 381560a320
documented preserve-recordsize flag 2019-09-25 18:33:55 +02:00
Christoph Klaffl a9043b5301
implemented flag for preserving recordsize on inital sends 2019-09-25 18:24:40 +02:00
Christoph Klaffl dc2371775a
don't use hardcoded paths 2019-06-06 08:25:18 +02:00
32 changed files with 2183 additions and 697 deletions

View File

@ -1,9 +1,49 @@
2.2.0 [overall] documentation updates, small fixes (@azmodude, @deviantintegral, @jimsalterjrs, @alexhaydock, @cbreak-black, @kd8bny, @JavaScriptDude, @veeableful, @rsheasby, @Topslakr, @mavhc, @adam-stamand, @joelishness, @jsoref, @dodexahedron, @phreaker0)
[syncoid] implemented flag for preserving properties without the zfs -p flag (@phreaker0)
[syncoid] implemented target snapshot deletion (@mat813)
[syncoid] support bookmarks which are taken in the same second (@delxg, @phreaker0)
[syncoid] exit with an error if the specified src dataset doesn't exist (@phreaker0)
[syncoid] rollback is now done implicitly instead of explicit (@jimsalterjrs, @phreaker0)
[syncoid] append a rand int to the socket name to prevent collisions with parallel invocations (@Gryd3)
[syncoid] implemented support for ssh_config(5) files (@endreszabo)
[syncoid] snapshot hold/unhold support (@rbike)
[sanoid] handle duplicate key definitions gracefully (@phreaker0)
[syncoid] implemented removal of conflicting snapshots with force-delete option (@phreaker0)
[sanoid] implemented pre pruning script hook (@phreaker0)
[syncoid] implemented direct connection support (bypass ssh) for the actual data transfer (@phreaker0)
2.1.0 [overall] documentation updates, small fixes (@HavardLine, @croadfeldt, @jimsalterjrs, @jim-perkins, @kr4z33, @phreaker0)
[syncoid] do not require user to be specified for syncoid (@aerusso)
[syncoid] implemented option for keeping sync snaps (@phreaker0)
[syncoid] use sudo if necessary for checking pool capabilities regarding resumable send (@phreaker0)
[syncoid] catch another case were the resume state isn't available anymore (@phreaker0)
[syncoid] check for an invalid argument combination (@phreaker0)
[syncoid] fix iszfsbusy check for similar dataset names (@phreaker0)
[syncoid] append timezone offset to the syncoid snapshot name to fix DST collisions (@phreaker0)
[packaging] post install script for debian package to remove old unused snapshot cache file (@phreaker0)
[syncoid] implemented fallback for listing snapshots on solaris (@phreaker0)
[sanoid] remove invalid locks (@phreaker0)
[packaging] removed debian dependency for systemd (@phreaker0)
[sanoid] move sanoid cache and lock files to subdirectories (@lopsided98)
[sanoid] remove 's in monitoring messages (@dlangille)
[findoid] reworked argument parsing and error out if file path is not provided (@phreaker0)
[findoid] also show current file version if available (@phreaker0)
[findoid] handle FileNotFound errors properly (@phreaker0)
[findoid] don't use hardcoded paths (@phreaker0)
[findoid] improve dataset detection by only including mounted datasets (@phreaker0)
[sanoid] pass more information to pre/post/prune scripts and execute them only once per dataset (@tiedotguy, @phreaker0)
[syncoid] implemented option for preserving recordsizes on initial replications (@phreaker0)
[syncoid] fixed send size estimation for latest FreeBSD versions (@phreaker0)
[syncoid] add ability to configure pv (@gdevenyi)
[sanoid] don't use hardcoded paths (@phreaker0)
[syncoid] gracefully handle error when source dataset disappeared (@mschout)
2.0.3 [sanoid] reverted DST handling and improved it as quickfix (@phreaker0)
2.0.2 [overall] documentation updates, new dependencies, small fixes, more warnings (@benyanke, @matveevandrey, @RulerOf, @klemens-u, @johnramsden, @danielewood, @g-a-c, @hartzell, @fryfrog, @phreaker0)
[sanoid] changed and simplified DST handling (@shodanshok)
[syncoid] reset partially resume state automatically (@phreaker0)
[syncoid] handle some zfs erros automatically by parsing the stderr outputs (@phreaker0)
[syncoid] handle some zfs errors automatically by parsing the stderr outputs (@phreaker0)
[syncoid] fixed ordering of snapshots with the same creation timestamp (@phreaker0)
[syncoid] don't use hardcoded paths (@phreaker0)
[syncoid] fix for special setup with listsnapshots=on (@phreaker0)
@ -20,7 +60,7 @@
[syncoid] option to change mbuffer size (@TerraTech)
[tests] fixes for FreeBSD (@phreaker0)
[sanoid] support for zfs recursion (@jMichaelA, @phreaker0)
[syncoid] fixed bookmark handling for volumens (@ppcontrib)
[syncoid] fixed bookmark handling for volumes (@ppcontrib)
[sanoid] allow time units for monitoring warn/crit values (@phreaker0)
2.0.1 [sanoid] fixed broken monthly warn/critical monitoring values in default template (@jimsalterjrs)
@ -58,7 +98,7 @@
[sanoid] implemented monitor-capacity flag for checking zpool capacity limits (@phreaker0)
[syncoid] Added support for ZStandard compression.(@danielewood)
[syncoid] implemented support for excluding datasets from replication with regular expressions (@phreaker0)
[syncoid] correctly parse zfs column output, fixes resumeable send with datasets containing spaces (@phreaker0)
[syncoid] correctly parse zfs column output, fixes resumable send with datasets containing spaces (@phreaker0)
[syncoid] added option for using extra identification in the snapshot name for replication to multiple targets (@phreaker0)
[syncoid] added option for skipping the parent dataset in recursive replication (@phreaker0)
[syncoid] typos (@UnlawfulMonad, @jsavikko, @phreaker0)
@ -92,12 +132,12 @@
replicating to target/parent/child2. This could still use some cleanup TBH; syncoid SHOULD exit 3
if any of these errors happen (to assist detection of errors in scripting) but now would exit 0.
1.4.12 Sanoid now strips trailing whitespace in template definitions in sanoid.conf, per Github #61
1.4.12 Sanoid now strips trailing whitespace in template definitions in sanoid.conf, per GitHub #61
1.4.11 enhanced Syncoid to use zfs `guid` property rather than `creation` property to ensure snapshots on source
and target actually match. This immediately prevents conflicts due to timezone differences on source and target,
and also paves the way in the future for Syncoid to find matching snapshots even after `zfs rename` on source
or target. Thank you Github user @mailinglists35 for the idea!
or target. Thank you GitHub user @mailinglists35 for the idea!
1.4.10 added --compress=pigz-fast and --compress=pigz-slow. On a Xeon E3-1231v3, pigz-fast is equivalent compression
to --compress=gzip but with compressed throughput of 75.2 MiB/s instead of 18.1 MiB/s. pigz-slow is around 5%
@ -215,4 +255,4 @@
1.0.1 ported slightly modified iszfsbusy sub from syncoid to sanoid (to keep from thinning snapshots during replications)
1.0.0 initial commit to Github
1.0.0 initial commit to GitHub

128
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

1
CONTRIBUTING.md Normal file
View File

@ -0,0 +1 @@
Any and all contributions made to this project must be compatible with the project's own GPLv3 license.

View File

@ -1,4 +1,10 @@
FreeBSD users will need to change the Perl shebangs at the top of the executables from #!/usr/bin/perl
Syncoid assumes a bourne style shell on remote hosts. Using (t)csh (the default for root under FreeBSD)
will cause syncoid to fail cryptically due to 2>&1 output redirects.
To use syncoid successfully with FreeBSD targets, you must use the chsh command to change the root shell:
root@bsd:~# chsh -s /bin/sh
FreeBSD users will also need to change the Perl shebangs at the top of the executables from #!/usr/bin/perl
to #!/usr/local/bin/perl in most cases.
Sorry folks, but if I set this with #!/usr/bin/env perl as suggested, then nothing works properly
@ -11,14 +17,3 @@ If you don't want to have to change the shebangs, your other option is to drop a
root@bsd:~# ln -s /usr/local/bin/perl /usr/bin/perl
After putting this symlink in place, ANY perl script shebanged for Linux will work on your system too.
Syncoid assumes a bourne style shell on remote hosts. Using (t)csh (the default for root under FreeBSD)
has some known issues:
* If mbuffer is present, syncoid will fail with an "Ambiguous output redirect." error. So if you:
root@bsd:~# ln -s /usr/local/bin/mbuffer /usr/bin/mbuffer
make sure the remote user is using an sh compatible shell.
To change to a compatible shell, use the chsh command:
root@bsd:~# chsh -s /bin/sh

View File

@ -8,6 +8,7 @@
- [Debian/Ubuntu](#debianubuntu)
- [CentOS](#centos)
- [FreeBSD](#freebsd)
- [Alpine Linux / busybox](#alpine-Linux-busybox-based-distributions)
- [Other OSes](#other-oses)
- [Configuration](#configuration)
- [Sanoid](#sanoid)
@ -21,39 +22,52 @@ Install prerequisite software:
```bash
apt install debhelper libcapture-tiny-perl libconfig-inifiles-perl pv lzop mbuffer
apt install debhelper libcapture-tiny-perl libconfig-inifiles-perl pv lzop mbuffer build-essential git
```
Clone this repo, build the debian package and install it (alternatively you can skip the package and do it manually like described below for CentOS):
```bash
# Download the repo as root to avoid changing permissions later
sudo git clone https://github.com/jimsalterjrs/sanoid.git
git clone https://github.com/jimsalterjrs/sanoid.git
cd sanoid
# checkout latest stable release or stay on master for bleeding edge stuff (but expect bugs!)
git checkout $(git tag | grep "^v" | tail -n 1)
ln -s packages/debian .
dpkg-buildpackage -uc -us
apt install ../sanoid_*_all.deb
sudo apt install ../sanoid_*_all.deb
```
Enable sanoid timer:
```bash
# enable and start the sanoid timer
sudo systemctl enable sanoid.timer
sudo systemctl start sanoid.timer
sudo systemctl enable --now sanoid.timer
```
## CentOS
## CentOS/RHEL
Install prerequisite software:
```bash
# Install and enable epel if we don't already have it, and git too
# Install and enable EPEL if we don't already have it, and git too:
# (Note that on RHEL we cannot enable EPEL with the epel-release
# package, so you should follow the instructions on the main EPEL site.)
sudo yum install -y epel-release git
# On CentOS, we also need to enable the PowerTools repo:
sudo yum config-manager --set-enabled powertools
# For Centos 8 you need to enable the PowerTools repo to make all the needed Perl modules available (Recommended)
sudo dnf config-manager --set-enabled powertools
# On RHEL, instead of PowerTools, we need to enable the CodeReady Builder repo:
sudo subscription-manager repos --enable=codeready-builder-for-rhel-8-x86_64-rpms
# Install the packages that Sanoid depends on:
sudo yum install -y perl-Config-IniFiles perl-Data-Dumper perl-Capture-Tiny lzop mbuffer mhash pv
sudo yum install -y perl-Config-IniFiles perl-Data-Dumper perl-Capture-Tiny perl-Getopt-Long lzop mbuffer mhash pv
# The repositories above should contain all the relevant Perl modules, but if you
# still cannot find them then you can install them from CPAN manually:
sudo dnf install perl-CPAN perl-CPAN
cpan # answer the questions and paste the following lines:
# install Capture::Tiny
# install Config::IniFiles
# install Getopt::Long
```
Clone this repo, then put the executables and config files into the appropriate directories:
@ -136,8 +150,7 @@ sudo systemctl daemon-reload
# Enable sanoid-prune.service to allow it to be triggered by sanoid.service
sudo systemctl enable sanoid-prune.service
# Enable and start the Sanoid timer
sudo systemctl enable sanoid.timer
sudo systemctl start sanoid.timer
sudo systemctl enable --now sanoid.timer
```
Now, proceed to configure [**Sanoid**](#configuration)
@ -147,7 +160,7 @@ Now, proceed to configure [**Sanoid**](#configuration)
Install prerequisite software:
```bash
pkg install p5-Config-Inifiles p5-Capture-Tiny pv mbuffer lzop
pkg install p5-Config-Inifiles p5-Capture-Tiny pv mbuffer lzop sanoid
```
**Additional notes:**
@ -156,7 +169,59 @@ pkg install p5-Config-Inifiles p5-Capture-Tiny pv mbuffer lzop
* Simplest path workaround is symlinks, eg `ln -s /usr/local/bin/lzop /usr/bin/lzop` or similar, as appropriate to create links in **/usr/bin** to wherever the utilities actually are on your system.
* See note about mbuffer and other things in FREEBSD.readme
* See note about tcsh unpleasantness and other things in FREEBSD.readme
## Alpine Linux / busybox based distributions
The busybox implementation of ps is lacking needed arguments so a proper ps program needs to be installed.
For Alpine Linux this can be done with:
`apk --no-cache add procps`
## MacOS
Install prerequisite software:
```
perl -MCPAN -e install Config::IniFiles
```
The crontab can be used as on a normal unix. To use launchd instead, this example config file can be use can be used. Modify it for your needs. In particular, adjust the sanoid path.
It will start sanoid once per hour, at minute 51. Missed invocations due to standby will be merged into a single invocation at the next wakeup.
```bash
cat << "EOF" | sudo tee /Library/LaunchDaemons/net.openoid.Sanoid.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>net.openoid.Sanoid</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/sanoid/sanoid</string>
<string>--cron</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>TZ</key>
<string>UTC</string>
<key>PATH</key>
<string>/usr/local/zfs/bin:$PATH:/usr/local/bin</string>
</dict>
<key>StartCalendarInterval</key>
<array>
<dict>
<key>Minute</key>
<integer>51</integer>
</dict>
</array>
</dict>
</plist>
EOF
sudo launchctl load /Library/LaunchDaemons/net.openoid.Sanoid.plist
```
## Other OSes
@ -171,6 +236,17 @@ pkg install p5-Config-Inifiles p5-Capture-Tiny pv mbuffer lzop
3. Create the config directory `/etc/sanoid` and put `sanoid.defaults.conf` in there, and create `sanoid.conf` in it too
4. Create a cron job or a systemd timer that runs `sanoid --cron` once per minute
## cron
If you use cron there is the need to ensure that only one instance of sanoid is run at any time (or else there will be funny error messages about missing snapshots, ...). It's also good practice to separate the snapshot taking and pruning so the later won't block the former in case of long running pruning operations. Following is the recommend setup for a standard install:
```
*/15 * * * * root flock -n /var/run/sanoid/cron-take.lock -c "TZ=UTC sanoid --take-snapshots"
*/15 * * * * root flock -n /var/run/sanoid/cron-prune.lock -c "sanoid --prune-snapshots"
```
Adapt the timer interval to the lowest configured snapshot interval.
# Configuration
**Sanoid** won't do anything useful unless you tell it how to handle your ZFS datasets in `/etc/sanoid/sanoid.conf`.
@ -182,3 +258,12 @@ pkg install p5-Config-Inifiles p5-Capture-Tiny pv mbuffer lzop
Take a look at the files `sanoid.defaults.conf` and `sanoid.conf` for all possible configuration options.
Also have a look at the README.md for a simpler suggestion for `sanoid.conf`.
## Syncoid
If you are pushing or pulling from a remote host, create a user with privileges to `ssh` as well as `sudo`. To ensure that `zfs send/receive` can execute, adjust the privileges of the user to execute `sudo` **without** a password for only the `zfs` binary (run `which zfs` to find the path of the `zfs` binary). Modify `/etc/sudoers` by running `# visudo`. Add the following line for your user.
```
...
<user> ALL=NOPASSWD: <path of zfs binary>
...
```

186
README.md
View File

@ -1,15 +1,26 @@
<p align="center"><img src="http://www.openoid.net/wp-content/themes/openoid/images/sanoid_logo.png" alt="sanoid logo" title="sanoid logo"></p>
<table align="center">
<tr>
<td border="1" width="750">
<p align="center">
<img src="http://www.openoid.net/wp-content/themes/openoid/images/sanoid_logo.png" alt="sanoid logo" title="sanoid logo">
</p>
<img src="https://openoid.net/gplv3-127x51.png" width=127 height=51 align="right">
<p align="left">Sanoid is provided to you completely free and libre, now and in perpetuity, via the GPL v3.0 license. If you find the project useful, please consider either a recurring or one-time donation at <a href="https://www.patreon.com/PracticalZFS" target="_blank">Patreon</a> or <a href="https://www.paypal.com/donate/?hosted_button_id=5BLPNV86D4S9N" target="_blank">PayPal</a>—your contributions will support both this project and the Practical ZFS <a href="https://discourse.practicalzfs.com/" target="_blank">forum</a>.
</p>
</td>
</tr>
</table>
<img src="http://openoid.net/gplv3-127x51.png" width=127 height=51 align="right">Sanoid is a policy-driven snapshot management tool for ZFS filesystems. When combined with the Linux KVM hypervisor, you can use it to make your systems <a href="http://openoid.net/transcend" target="_blank">functionally immortal</a>.
Sanoid is a policy-driven snapshot management tool for ZFS filesystems. When combined with the Linux KVM hypervisor, you can use it to make your systems <a href="https://openoid.net/transcend" target="_blank">functionally immortal</a> via automated snapshot management and over-the-air replication.
<p align="center"><a href="https://youtu.be/ZgowLNBsu00" target="_blank"><img src="http://www.openoid.net/sanoid_video_launcher.png" alt="sanoid rollback demo" title="sanoid rollback demo"></a><br clear="all"><sup>(Real time demo: rolling back a full-scale cryptomalware infection in seconds!)</sup></p>
More prosaically, you can use Sanoid to create, automatically thin, and monitor snapshots and pool health from a single eminently human-readable TOML config file at /etc/sanoid/sanoid.conf. (Sanoid also requires a "defaults" file located at /etc/sanoid/sanoid.defaults.conf, which is not user-editable.) A typical Sanoid system would have a single cron job:
More prosaically, you can use Sanoid to create, automatically thin, and monitor snapshots and pool health from a single eminently human-readable TOML config file at /etc/sanoid/sanoid.conf. (Sanoid also requires a "defaults" file located at /etc/sanoid/sanoid.defaults.conf, which is not user-editable.) A typical Sanoid system would have a single cron job but see INSTALL.md for more details:
```
* * * * * TZ=UTC /usr/local/bin/sanoid --cron
```
`Note`: Using UTC as timezone is recommend to prevent problems with daylight saving times
`Note`: Using UTC as timezone is recommended to prevent problems with daylight saving times
And its /etc/sanoid/sanoid.conf might look something like this:
@ -39,6 +50,10 @@ And its /etc/sanoid/sanoid.conf might look something like this:
Which would be enough to tell sanoid to take and keep 36 hourly snapshots, 30 dailies, 3 monthlies, and no yearlies for all datasets under data/images (but not data/images itself, since process_children_only is set). Except in the case of data/images/win7, which follows the same template (since it's a child of data/images) but only keeps 4 hourlies for whatever reason.
For more full details on sanoid.conf settings see [Wiki page](https://github.com/jimsalterjrs/sanoid/wiki/Sanoid#options).
**Note**: Be aware that if you don't specify some interval options the defaults will be used (from /etc/sanoid/sanoid.defaults.conf)
##### Sanoid Command Line Options
+ --cron
@ -91,7 +106,7 @@ Which would be enough to tell sanoid to take and keep 36 hourly snapshots, 30 da
+ --quiet
Supress non-error output.
Suppress non-error output.
+ --verbose
@ -99,7 +114,7 @@ Which would be enough to tell sanoid to take and keep 36 hourly snapshots, 30 da
+ --debug
This prints out quite alot of additional information during a sanoid run, and is normally not needed.
This prints out quite a lot of additional information during a sanoid run, and is normally not needed.
+ --readonly
@ -109,6 +124,100 @@ Which would be enough to tell sanoid to take and keep 36 hourly snapshots, 30 da
Show help message.
### Sanoid script hooks
There are three script types which can optionally be executed at various stages in the lifecycle of a snapshot:
#### `pre_snapshot_script`
Will be executed before the snapshot(s) of a single dataset are taken. The following environment variables are passed:
| Env vars | Description |
| ----------------- | ----------- |
| `SANOID_SCRIPT` | The type of script being executed, one of `pre`, `post`, or `prune`. Allows for one script to be used for multiple tasks |
| `SANOID_TARGET` | **DEPRECATED** The dataset about to be snapshot (only the first dataset will be provided) |
| `SANOID_TARGETS` | Comma separated list of all datasets to be snapshotted (currently only a single dataset, multiple datasets will be possible later with atomic groups) |
| `SANOID_SNAPNAME` | **DEPRECATED** The name of the snapshot that will be taken (only the first name will be provided, does not include the dataset name) |
| `SANOID_SNAPNAMES` | Comma separated list of all snapshot names that will be taken (does not include the dataset name) |
| `SANOID_TYPES` | Comma separated list of all snapshot types to be taken (yearly, monthly, weekly, daily, hourly, frequently) |
If the script returns a non-zero exit code, the snapshot(s) will not be taken unless `no_inconsistent_snapshot` is false.
#### `post_snapshot_script`
Will be executed when:
- The pre-snapshot script succeeded or
- The pre-snapshot script failed and `force_post_snapshot_script` is true.
| Env vars | Description |
| -------------------- | ----------- |
| `SANOID_SCRIPT` | as above |
| `SANOID_TARGET` | **DEPRECATED** as above |
| `SANOID_TARGETS` | as above |
| `SANOID_SNAPNAME` | **DEPRECATED** as above |
| `SANOID_SNAPNAMES` | as above |
| `SANOID_TYPES` | as above |
| `SANOID_PRE_FAILURE` | This will indicate if the pre-snapshot script failed |
#### `pruning_script`
Will be executed after a snapshot is successfully deleted. The following environment variables will be passed:
| Env vars | Description |
| ----------------- | ----------- |
| `SANOID_SCRIPT` | as above |
| `SANOID_TARGET` | as above |
| `SANOID_SNAPNAME` | as above |
#### example
**sanoid.conf**:
```
...
[sanoid-test-0]
use_template = production
recursive = yes
pre_snapshot_script = /tmp/debug.sh
post_snapshot_script = /tmp/debug.sh
pruning_script = /tmp/debug.sh
...
```
**verbose sanoid output**:
```
...
executing pre_snapshot_script '/tmp/debug.sh' on dataset 'sanoid-test-0'
taking snapshot sanoid-test-0@autosnap_2020-02-12_14:49:33_yearly
taking snapshot sanoid-test-0@autosnap_2020-02-12_14:49:33_monthly
taking snapshot sanoid-test-0@autosnap_2020-02-12_14:49:33_daily
taking snapshot sanoid-test-0@autosnap_2020-02-12_14:49:33_hourly
executing post_snapshot_script '/tmp/debug.sh' on dataset 'sanoid-test-0'
...
```
**pre script env variables**:
```
SANOID_SCRIPT=pre
SANOID_TARGET=sanoid-test-0/b/bb
SANOID_TARGETS=sanoid-test-0/b/bb
SANOID_SNAPNAME=autosnap_2020-02-12_14:49:32_yearly
SANOID_SNAPNAMES=autosnap_2020-02-12_14:49:32_yearly,autosnap_2020-02-12_14:49:32_monthly,autosnap_2020-02-12_14:49:32_daily,autosnap_2020-02-12_14:49:32_hourly
SANOID_TYPES=yearly,monthly,daily,hourly
```
**post script env variables**:
```
SANOID_SCRIPT=post
SANOID_TARGET=sanoid-test-0/b/bb
SANOID_TARGETS=sanoid-test-0/b/bb
SANOID_SNAPNAME=autosnap_2020-02-12_14:49:32_yearly
SANOID_SNAPNAMES=autosnap_2020-02-12_14:49:32_yearly,autosnap_2020-02-12_14:49:32_monthly,autosnap_2020-02-12_14:49:32_daily,autosnap_2020-02-12_14:49:32_hourly
SANOID_TYPES=yearly,monthly,daily,hourly
SANOID_PRE_FAILURE=0
```
----------
# Syncoid
@ -134,7 +243,7 @@ syncoid root@remotehost:data/images/vm backup/images/vm
Which would pull-replicate the filesystem from the remote host to the local system over an SSH tunnel.
Syncoid supports recursive replication (replication of a dataset and all its child datasets) and uses mbuffer buffering, lzop compression, and pv progress bars if the utilities are available on the systems used.
If ZFS supports resumeable send/receive streams on both the source and target those will be enabled as default.
If ZFS supports resumable send/receive streams on both the source and target those will be enabled as default.
As of 1.4.18, syncoid also automatically supports and enables resume of interrupted replication when both source and target support this feature.
@ -176,7 +285,7 @@ As of 1.4.18, syncoid also automatically supports and enables resume of interrup
+ --identifier=
Adds the given identifier to the snapshot name after "syncoid_" prefix and before the hostname. This enables the use case of reliable replication to multiple targets from the same host. The following chars are allowed: a-z, A-Z, 0-9, _, -, : and . .
Adds the given identifier to the snapshot and hold name after "syncoid_" prefix and before the hostname. This enables the use case of reliable replication to multiple targets from the same host. The following chars are allowed: a-z, A-Z, 0-9, _, -, : and . .
+ -r --recursive
@ -188,15 +297,15 @@ As of 1.4.18, syncoid also automatically supports and enables resume of interrup
+ --compress <compression type>
Currently accepted options: gzip, pigz-fast, pigz-slow, zstd-fast, zstd-slow, lz4, xz, lzo (default) & none. If the selected compression method is unavailable on the source and destination, no compression will be used.
Compression method to use for network transfer. Currently accepted options: gzip, pigz-fast, pigz-slow, zstd-fast, zstd-slow, lz4, xz, lzo (default) & none. If the selected compression method is unavailable on the source and destination, no compression will be used.
+ --source-bwlimit <limit t|g|m|k>
This is the bandwidth limit in bytes (kbytes, mbytes, etc) per second imposed upon the source. This is mainly used if the target does not have mbuffer installed, but bandwidth limits are desired.
This is the bandwidth limit in bytes (kbytes, mbytes, etc) per second imposed upon the source. This is mainly used if the target does not have mbuffer installed, but bandwidth limits are desired.
+ --target-bw-limit <limit t|g|m|k>
+ --target-bwlimit <limit t|g|m|k>
This is the bandwidth limit in bytes (kbytes, mbytesm etc) per second imposed upon the target. This is mainly used if the source does not have mbuffer installed, but bandwidth limits are desired.
This is the bandwidth limit in bytes (kbytes, mbytes, etc) per second imposed upon the target. This is mainly used if the source does not have mbuffer installed, but bandwidth limits are desired.
+ --no-command-checks
@ -210,9 +319,29 @@ As of 1.4.18, syncoid also automatically supports and enables resume of interrup
This argument tells syncoid to restrict itself to existing snapshots, instead of creating a semi-ephemeral syncoid snapshot at execution time. Especially useful in multi-target (A->B, A->C) replication schemes, where you might otherwise accumulate a large number of foreign syncoid snapshots.
+ --keep-sync-snap
This argument tells syncoid to skip pruning old snapshots created and used by syncoid for replication if '--no-sync-snap' isn't specified.
+ --create-bookmark
This argument tells syncoid to create a zfs bookmark for the newest snapshot after it got replicated successfully. The bookmark name will be equal to the snapshot name. Only works in combination with the --no-sync-snap option. This can be very useful for irregular replication where the last matching snapshot on the source was already deleted but the bookmark remains so a replication is still possible.
This argument tells syncoid to create a zfs bookmark for the newest snapshot after it got replicated successfully. The bookmark name will be equal to the snapshot name. Only works in combination with the --no-sync-snap option. This can be very useful for irregular replication where the last matching snapshot on the source was already deleted but the bookmark remains so a replication is still possible.
+ --use-hold
This argument tells syncoid to add a hold to the newest snapshot on the source and target after replication succeeds and to remove the hold after the next successful replication. Setting a hold prevents the snapshots from being destroyed. The hold name includes the identifier if set. This allows for separate holds in case of replication to multiple targets.
+ --preserve-recordsize
This argument tells syncoid to set the recordsize on the target before writing any data to it matching the one set on the replication src. This only applies to initial sends.
+ --preserve-properties
This argument tells syncoid to get all locally set dataset properties from the source and apply all supported ones on the target before writing any data. It's similar to the '-p' flag for zfs send but also works for encrypted datasets in non raw sends. This only applies to initial sends.
+ --delete-target-snapshots
With this argument snapshots which are missing on the source will be destroyed on the target. Use this if you only want to handle snapshots on the source.
Note that snapshot deletion is only done after a successful synchronization. If no new snapshots are found, no synchronization is done and no deletion either.
+ --no-clone-rollback
@ -224,19 +353,33 @@ As of 1.4.18, syncoid also automatically supports and enables resume of interrup
+ --exclude=REGEX
The given regular expression will be matched against all datasets which would be synced by this run and excludes them. This argument can be specified multiple times.
__DEPRECATION NOTICE:__ `--exclude` has been deprecated and will be removed in a future release. Please use `--exclude-datasets` instead.
The given regular expression will be matched against all datasets which would be synced by this run and excludes them. This argument can be specified multiple times. The provided regex pattern is matched against the dataset name only; this option does not affect which snapshots are synchronized. If both `--exclude` and `--exclude-datasets` are provided, then `--exclude` is ignored.
+ --exclude-datasets=REGEX
The given regular expression will be matched against all datasets which would be synced by this run and excludes them. This argument can be specified multiple times. The provided regex pattern is matched against the dataset name only; this option does not affect which snapshots are synchronized.
+ --exclude-snaps=REGEX
Exclude specific snapshots that match the given regular expression. The provided regex pattern is matched against the snapshot name only. Can be specified multiple times. If a snapshot matches both the exclude-snaps and include-snaps patterns, then it will be excluded.
+ --include-snaps=REGEX
Only include snapshots that match the given regular expression. The provided regex pattern is matched against the snapshot name only. Can be specified multiple times. If a snapshot matches both the exclude-snaps and include-snaps patterns, then it will be excluded.
+ --no-resume
This argument tells syncoid to not use resumeable zfs send/receive streams.
This argument tells syncoid to not use resumable zfs send/receive streams.
+ --force-delete
Remove target datasets recursively (WARNING: this will also affect child datasets with matching snapshots/bookmarks), if there are no matching snapshots/bookmarks.
Remove target datasets recursively (WARNING: this will also affect child datasets with matching snapshots/bookmarks), if there are no matching snapshots/bookmarks. Also removes conflicting snapshots if the replication would fail because of a snapshot which has the same name between source and target but different contents.
+ --no-clone-handling
This argument tells syncoid to not recreate clones on the targe on initial sync and doing a normal replication instead.
This argument tells syncoid to not recreate clones on the target on initial sync, and do a normal replication instead.
+ --dumpsnaps
@ -262,13 +405,18 @@ As of 1.4.18, syncoid also automatically supports and enables resume of interrup
Use specified identity file as per ssh -i.
+ --insecure-direct-connection=IP:PORT[,IP:PORT,[TIMEOUT,[mbuffer]]]
WARNING: This is an insecure option as the data is not encrypted while being sent over the network. Only use if you trust the complete network path.
Use a direct tcp connection (with socat and busybox nc/mbuffer) for the actual zfs send/recv stream. All control commands are still executed via the ssh connection. The first address pair is used for connecting to the target host from the source host and the second pair is for listening on the target host. If the later isn't provided the same as the former is used. This can be used for saturating high throughput connection like >= 10GBe network which isn't easy with the overhead off ssh. It can also be useful for encrypted datasets to lower the cpu usage needed for replication but be aware that metadata is NOT ENCRYPTED in this case. The default timeout is 60 seconds and can be overridden by providing it as third argument. By default busybox nc is used for the listeing tcp socket, if mbuffer is preferred specify its name as fourth argument but be aware that mbuffer listens on all interfaces and uses an optionally provided ip address for access restriction (This option can't be used for relaying between two remote hosts)
+ --quiet
Supress non-error output.
Suppress non-error output.
+ --debug
This prints out quite alot of additional information during a sanoid run, and is normally not needed.
This prints out quite a lot of additional information during a syncoid run, and is normally not needed.
+ --help

13
SECURITY.md Normal file
View File

@ -0,0 +1,13 @@
# Security Policy
## Supported Versions
The Sanoid project directly supports both the code in the main branch, and the last two releases found here on GitHub.
Community support is available for all versions, with the understanding that in some cases "upgrade to a newer version" may be the support offered.
If you've installed Sanoid from your distribution's repositories, we're happy to offer community support with the same caveat!
## Reporting a Vulnerability
If you believe you've found a serious security vulnerability in Sanoid, please create an Issue here on GitHub. If you prefer a private contact channel to disclose
particularly sensitive or private details, you may request one in the GitHub Issue you create.

View File

@ -1 +1 @@
2.0.3
2.2.0

View File

@ -4,7 +4,7 @@
# from http://www.gnu.org/licenses/gpl-3.0.html on 2014-11-17. A copy should also be available in this
# project's Git repository at https://github.com/jimsalterjrs/sanoid/blob/master/LICENSE.
$::VERSION = '2.0.2';
$::VERSION = '2.2.0';
use strict;
use warnings;
@ -25,6 +25,9 @@ if ($args{'path'} eq '') {
}
}
# resolve given path to a canonical one
$args{'path'} = Cwd::realpath($args{'path'});
my $dataset = getdataset($args{'path'});
my %versions = getversions($args{'path'}, $dataset);

View File

@ -1,3 +1,51 @@
sanoid (2.2.0) unstable; urgency=medium
[overall] documentation updates, small fixes (@azmodude, @deviantintegral, @jimsalterjrs, @alexhaydock, @cbreak-black, @kd8bny, @JavaScriptDude, @veeableful, @rsheasby, @Topslakr, @mavhc, @adam-stamand, @joelishness, @jsoref, @dodexahedron, @phreaker0)
[syncoid] implemented flag for preserving properties without the zfs -p flag (@phreaker0)
[syncoid] implemented target snapshot deletion (@mat813)
[syncoid] support bookmarks which are taken in the same second (@delxg, @phreaker0)
[syncoid] exit with an error if the specified src dataset doesn't exist (@phreaker0)
[syncoid] rollback is now done implicitly instead of explicit (@jimsalterjrs, @phreaker0)
[syncoid] append a rand int to the socket name to prevent collisions with parallel invocations (@Gryd3)
[syncoid] implemented support for ssh_config(5) files (@endreszabo)
[syncoid] snapshot hold/unhold support (@rbike)
[sanoid] handle duplicate key definitions gracefully (@phreaker0)
[syncoid] implemented removal of conflicting snapshots with force-delete option (@phreaker0)
[sanoid] implemented pre pruning script hook (@phreaker0)
[syncoid] implemented direct connection support (bypass ssh) for the actual data transfer (@phreaker0)
-- Jim Salter <github@jrs-s.net> Tue, 18 Jul 2023 10:04:00 +0200
sanoid (2.1.0) unstable; urgency=medium
[overall] documentation updates, small fixes (@HavardLine, @croadfeldt, @jimsalterjrs, @jim-perkins, @kr4z33, @phreaker0)
[syncoid] do not require user to be specified for syncoid (@aerusso)
[syncoid] implemented option for keeping sync snaps (@phreaker0)
[syncoid] use sudo if necessary for checking pool capabilities regarding resumable send (@phreaker0)
[syncoid] catch another case were the resume state isn't available anymore (@phreaker0)
[syncoid] check for an invalid argument combination (@phreaker0)
[syncoid] fix iszfsbusy check for similar dataset names (@phreaker0)
[syncoid] append timezone offset to the syncoid snapshot name to fix DST collisions (@phreaker0)
[packaging] post install script for debian package to remove old unused snapshot cache file (@phreaker0)
[syncoid] implemented fallback for listing snapshots on solaris (@phreaker0)
[sanoid] remove invalid locks (@phreaker0)
[packaging] removed debian dependency for systemd (@phreaker0)
[sanoid] move sanoid cache and lock files to subdirectories (@lopsided98)
[sanoid] remove 's in monitoring messages (@dlangille)
[findoid] reworked argument parsing and error out if file path is not provided (@phreaker0)
[findoid] also show current file version if available (@phreaker0)
[findoid] handle FileNotFound errors properly (@phreaker0)
[findoid] don't use hardcoded paths (@phreaker0)
[findoid] improve dataset detection by only including mounted datasets (@phreaker0)
[sanoid] pass more information to pre/post/prune scripts and execute them only once per dataset (@tiedotguy, @phreaker0)
[syncoid] implemented option for preserving recordsizes on initial replications (@phreaker0)
[syncoid] fixed send size estimation for latest FreeBSD versions (@phreaker0)
[syncoid] add ability to configure pv (@gdevenyi)
[sanoid] don't use hardcoded paths (@phreaker0)
[syncoid] gracefully handle error when source dataset disappeared (@mschout)
-- Jim Salter <github@jrs-s.net> Tue, 24 Nov 2020 11:47:00 +0100
sanoid (2.0.3) unstable; urgency=medium
[sanoid] reverted DST handling and improved it as quickfix (@phreaker0)
@ -9,7 +57,7 @@ sanoid (2.0.2) unstable; urgency=medium
[overall] documentation updates, new dependencies, small fixes, more warnings (@benyanke, @matveevandrey, @RulerOf, @klemens-u, @johnramsden, @danielewood, @g-a-c, @hartzell, @fryfrog, @phreaker0)
[syncoid] changed and simplified DST handling (@shodanshok)
[syncoid] reset partially resume state automatically (@phreaker0)
[syncoid] handle some zfs erros automatically by parsing the stderr outputs (@phreaker0)
[syncoid] handle some zfs errors automatically by parsing the stderr outputs (@phreaker0)
[syncoid] fixed ordering of snapshots with the same creation timestamp (@phreaker0)
[syncoid] don't use hardcoded paths (@phreaker0)
[syncoid] fix for special setup with listsnapshots=on (@phreaker0)
@ -72,7 +120,7 @@ sanoid (2.0.0) unstable; urgency=medium
[sanoid] implemented monitor-capacity flag for checking zpool capacity limits (@phreaker0)
[syncoid] Added support for ZStandard compression.(@danielewood)
[syncoid] implemented support for excluding datasets from replication with regular expressions (@phreaker0)
[syncoid] correctly parse zfs column output, fixes resumeable send with datasets containing spaces (@phreaker0)
[syncoid] correctly parse zfs column output, fixes resumable send with datasets containing spaces (@phreaker0)
[syncoid] added option for using extra identification in the snapshot name for replication to multiple targets (@phreaker0)
[syncoid] added option for skipping the parent dataset in recursive replication (@phreaker0)
[syncoid] typos (@UnlawfulMonad, @jsavikko, @phreaker0)

View File

@ -12,7 +12,7 @@ Package: sanoid
Architecture: all
Depends: libcapture-tiny-perl,
libconfig-inifiles-perl,
zfsutils-linux | zfs,
zfsutils-linux | zfs | openzfs-zfsutils,
${misc:Depends},
${perl:Depends}
Recommends: gzip,

Binary file not shown.

View File

@ -1,4 +1,4 @@
%global version 2.0.3
%global version 2.2.0
%global git_tag v%{version}
# Enable with systemctl "enable sanoid.timer"
@ -111,11 +111,15 @@ echo "* * * * * root %{_sbindir}/sanoid --cron" > %{buildroot}%{_docdir}/%{name}
%endif
%changelog
* Tue Jul 18 2023 Christoph Klaffl <christoph@phreaker.eu> - 2.2.0
- Bump to 2.2.0
* Tue Nov 24 2020 Christoph Klaffl <christoph@phreaker.eu> - 2.1.0
- Bump to 2.1.0
* Wed Oct 02 2019 Christoph Klaffl <christoph@phreaker.eu> - 2.0.3
- Bump to 2.0.3
* Wed Sep 25 2019 Christoph Klaffl <christoph@phreaker.eu> - 2.0.2
- Bump to 2.0.2
* Wed Dec 04 2018 Christoph Klaffl <christoph@phreaker.eu> - 2.0.0
* Tue Dec 04 2018 Christoph Klaffl <christoph@phreaker.eu> - 2.0.0
- Bump to 2.0.0
* Sat Apr 28 2018 Dominic Robinson <github@dcrdev.com> - 1.4.18-1
- Bump to 1.4.18

View File

@ -1 +0,0 @@
cf0ec23c310d2f9416ebabe48f5edb73 sanoid-1.4.18.tar.gz

368
sanoid
View File

@ -4,7 +4,7 @@
# from http://www.gnu.org/licenses/gpl-3.0.html on 2014-11-17. A copy should also be available in this
# project's Git repository at https://github.com/jimsalterjrs/sanoid/blob/master/LICENSE.
$::VERSION = '2.0.3';
$::VERSION = '2.2.0';
my $MINIMUM_DEFAULTS_VERSION = 2;
use strict;
@ -30,39 +30,86 @@ GetOptions(\%args, "verbose", "debug", "cron", "readonly", "quiet",
) or pod2usage(2);
# If only config directory (or nothing) has been specified, default to --cron --verbose
if (keys %args < 2) {
if (keys %args < 4) {
$args{'cron'} = 1;
$args{'verbose'} = 1;
}
my $pscmd = '/bin/ps';
# for compatibility reasons, older versions used hardcoded command paths
$ENV{'PATH'} = $ENV{'PATH'} . ":/bin:/sbin";
my $zfs = '/sbin/zfs';
my $zpool = '/sbin/zpool';
my $pscmd = 'ps';
my $zfs = 'zfs';
my $zpool = 'zpool';
my $conf_file = "$args{'configdir'}/sanoid.conf";
my $default_conf_file = "$args{'configdir'}/sanoid.defaults.conf";
# parse config file
my %config = init($conf_file,$default_conf_file);
my $cache_dir = $args{'cache-dir'};
my $run_dir = $args{'run-dir'};
make_path($cache_dir);
make_path($run_dir);
# if we call getsnaps(%config,1) it will forcibly update the cache, TTL or no TTL
my $forcecacheupdate = 0;
my $cacheTTL = 1200; # 20 minutes
# Allow a much older snapshot cache file than default if _only_ "--monitor-*" action commands are given
# (ignore "--verbose", "--configdir" etc)
if (
(
$args{'monitor-snapshots'}
|| $args{'monitor-health'}
|| $args{'monitor-capacity'}
) && ! (
$args{'cron'}
|| $args{'force-update'}
|| $args{'take-snapshots'}
|| $args{'prune-snapshots'}
|| $args{'force-prune'}
)
) {
# The command combination above must not assert true for any command that takes or prunes snapshots
$cacheTTL = 18000; # 5 hours
if ($args{'debug'}) { print "DEBUG: command combo means that the cache file (provided it exists) will be allowed to be older than default.\n"; }
}
# snapshot cache
my $cache = "$cache_dir/snapshots.txt";
my $cacheTTL = 900; # 15 minutes
my %snaps = getsnaps( \%config, $cacheTTL, $forcecacheupdate );
# configured dataset cache
my $cachedatasetspath = "$cache_dir/datasets.txt";
my @cachedatasets;
# parse config file
my %config = init($conf_file,$default_conf_file);
my %pruned;
my %capacitycache;
my %snapsbytype = getsnapsbytype( \%config, \%snaps );
my %snaps;
my %snapsbytype;
my %snapsbypath;
my %snapsbypath = getsnapsbypath( \%config, \%snaps );
# get snapshot list only if needed
if ($args{'monitor-snapshots'}
|| $args{'monitor-health'}
|| $args{'cron'}
|| $args{'take-snapshots'}
|| $args{'prune-snapshots'}
|| $args{'force-update'}
|| $args{'debug'}
) {
my $forcecacheupdate = 0;
if ($args{'force-update'}) {
$forcecacheupdate = 1;
}
%snaps = getsnaps( \%config, $cacheTTL, $forcecacheupdate);
%snapsbytype = getsnapsbytype( \%config, \%snaps );
%snapsbypath = getsnapsbypath( \%config, \%snaps );
}
# let's make it a little easier to be consistent passing these hashes in the same order to each sub
my @params = ( \%config, \%snaps, \%snapsbytype, \%snapsbypath );
@ -71,7 +118,6 @@ if ($args{'debug'}) { $args{'verbose'}=1; blabber (@params); }
if ($args{'monitor-snapshots'}) { monitor_snapshots(@params); }
if ($args{'monitor-health'}) { monitor_health(@params); }
if ($args{'monitor-capacity'}) { monitor_capacity(@params); }
if ($args{'force-update'}) { my $snaps = getsnaps( \%config, $cacheTTL, 1 ); }
if ($args{'cron'}) {
if ($args{'quiet'}) { $args{'verbose'} = 0; }
@ -127,7 +173,7 @@ sub monitor_snapshots {
my ($config, $snaps, $snapsbytype, $snapsbypath) = @_;
my %datestamp = get_date();
my $errorlevel = 0;
my $errlevel = 0;
my $msg;
my @msgs;
my @paths;
@ -166,7 +212,7 @@ sub monitor_snapshots {
my $dispcrit = displaytime($crit);
if ( $elapsed > $crit || $elapsed == -1) {
if ($crit > 0) {
if (! $config{$section}{'monitor_dont_crit'}) { $errorlevel = 2; }
if (! $config{$section}{'monitor_dont_crit'}) { $errlevel = 2; }
if ($elapsed == -1) {
push @msgs, "CRIT: $path has no $type snapshots at all!";
} else {
@ -175,7 +221,7 @@ sub monitor_snapshots {
}
} elsif ($elapsed > $warn) {
if ($warn > 0) {
if (! $config{$section}{'monitor_dont_warn'} && ($errorlevel < 2) ) { $errorlevel = 1; }
if (! $config{$section}{'monitor_dont_warn'} && ($errlevel < 2) ) { $errlevel = 1; }
push @msgs, "WARN: $path newest $type snapshot is $dispelapsed old (should be < $dispwarn)";
}
} else {
@ -193,7 +239,7 @@ sub monitor_snapshots {
if ($msg eq '') { $msg = "OK: all monitored datasets \($paths\) have fresh snapshots"; }
print "$msg\n";
exit $errorlevel;
exit $errlevel;
}
@ -262,7 +308,6 @@ sub prune_snapshots {
my ($config, $snaps, $snapsbytype, $snapsbypath) = @_;
my %datestamp = get_date();
my $forcecacheupdate = 0;
foreach my $section (keys %config) {
if ($section =~ /^template/) { next; }
@ -316,6 +361,25 @@ sub prune_snapshots {
if (checklock('sanoid_pruning')) {
writelock('sanoid_pruning');
foreach my $snap( @prunesnaps ){
my $dataset = (split '@', $snap)[0];
my $snapname = (split '@', $snap)[1];
if (! $args{'readonly'} && $config{$dataset}{'pre_pruning_script'}) {
$ENV{'SANOID_TARGET'} = $dataset;
$ENV{'SANOID_SNAPNAME'} = $snapname;
if ($args{'verbose'}) { print "executing pre_pruning_script '".$config{$dataset}{'pre_pruning_script'}."' on dataset '$dataset'\n"; }
my $ret = runscript('pre_pruning_script', $dataset);
delete $ENV{'SANOID_TARGET'};
delete $ENV{'SANOID_SNAPNAME'};
if ($ret != 0) {
# warning was already thrown by runscript function
# skip pruning if pre snapshot script returns non zero exit code
next;
}
}
if ($args{'verbose'}) { print "INFO: pruning $snap ... \n"; }
if (!$args{'force-prune'} && iszfsbusy($path)) {
if ($args{'verbose'}) { print "INFO: deferring pruning of $snap - $path is currently in zfs send or receive.\n"; }
@ -323,16 +387,16 @@ sub prune_snapshots {
if (! $args{'readonly'}) {
if (system($zfs, "destroy", $snap) == 0) {
$pruned{$snap} = 1;
my $dataset = (split '@', $snap)[0];
my $snapname = (split '@', $snap)[1];
if ($config{$dataset}{'pruning_script'}) {
$ENV{'SANOID_TARGET'} = $dataset;
$ENV{'SANOID_SNAPNAME'} = $snapname;
$ENV{'SANOID_SCRIPT'} = 'prune';
if ($args{'verbose'}) { print "executing pruning_script '".$config{$dataset}{'pruning_script'}."' on dataset '$dataset'\n"; }
my $ret = runscript('pruning_script',$dataset);
delete $ENV{'SANOID_TARGET'};
delete $ENV{'SANOID_SNAPNAME'};
delete $ENV{'SANOID_SCRIPT'};
}
} else {
warn "could not remove $snap : $?";
@ -367,7 +431,7 @@ sub take_snapshots {
my %datestamp = get_date();
my $forcecacheupdate = 0;
my @newsnaps;
my %newsnapsgroup;
# get utc timestamp of the current day for DST check
my $daystartUtc = timelocal(0, 0, 0, $datestamp{'mday'}, ($datestamp{'mon'}-1), $datestamp{'year'});
@ -389,9 +453,9 @@ sub take_snapshots {
if ($config{$section}{'process_children_only'}) { next; }
my $path = $config{$section}{'path'};
my @types = ('yearly','monthly','weekly','daily','hourly','frequently');
my @types = ('yearly','monthly','weekly','daily','hourly','frequently');
foreach my $type (@types) {
foreach my $type (@types) {
if ($config{$section}{$type} > 0) {
my $newestage; # in seconds
@ -511,55 +575,58 @@ sub take_snapshots {
my $maxage = time()-$lastpreferred;
if ( $newestage > $maxage ) {
# update to most current possible datestamp
%datestamp = get_date();
# print "we should have had a $type snapshot of $path $maxage seconds ago; most recent is $newestage seconds old.\n";
my $flags = "";
# use zfs (atomic) recursion if specified in config
if ($config{$section}{'zfs_recursion'}) {
$flags .= "r";
}
if ($handleDst) {
$flags .= "d";
if (!exists $newsnapsgroup{$path}) {
$newsnapsgroup{$path} = {
'recursive' => $config{$section}{'zfs_recursion'},
'handleDst' => $handleDst,
'datasets' => [$path], # for later atomic grouping, currently only a one element array
'types' => []
};
}
if ($flags ne "") {
push(@newsnaps, "$path\@autosnap_$datestamp{'sortable'}_$type\@$flags");
} else {
push(@newsnaps, "$path\@autosnap_$datestamp{'sortable'}_$type");
}
push(@{$newsnapsgroup{$path}{'types'}}, $type);
}
}
}
}
if ( (scalar(@newsnaps)) > 0) {
foreach my $snap ( @newsnaps ) {
my $extraMessage = "";
my @split = split '@', $snap, -1;
my $recursiveFlag = 0;
my $dstHandling = 0;
if (scalar(@split) == 3) {
my $flags = $split[2];
if (index($flags, "r") != -1) {
$recursiveFlag = 1;
$extraMessage = " (zfs recursive)";
chop $snap;
}
if (index($flags, "d") != -1) {
$dstHandling = 1;
chop $snap;
}
chop $snap;
if (%newsnapsgroup) {
while ((my $path, my $snapData) = each(%newsnapsgroup)) {
my $recursiveFlag = $snapData->{recursive};
my $dstHandling = $snapData->{handleDst};
my @datasets = @{$snapData->{datasets}};
my $dataset = $datasets[0];
my @types = @{$snapData->{types}};
# same timestamp for all snapshots types (daily, hourly, ...)
my %datestamp = get_date();
my @snapshots;
foreach my $type (@types) {
my $snapname = "autosnap_$datestamp{'sortable'}_$type";
push(@snapshots, $snapname);
}
my $dataset = $split[0];
my $snapname = $split[1];
my $datasetString = join(",", @datasets);
my $typeString = join(",", @types);
my $snapshotString = join(",", @snapshots);
my $extraMessage = "";
if ($recursiveFlag) {
$extraMessage = " (zfs recursive)";
}
my $presnapshotfailure = 0;
my $ret = 0;
if ($config{$dataset}{'pre_snapshot_script'}) {
$ENV{'SANOID_TARGET'} = $dataset;
$ENV{'SANOID_SNAPNAME'} = $snapname;
$ENV{'SANOID_TARGETS'} = $datasetString;
$ENV{'SANOID_SNAPNAME'} = $snapshots[0];
$ENV{'SANOID_SNAPNAMES'} = $snapshotString;
$ENV{'SANOID_TYPES'} = $typeString;
$ENV{'SANOID_SCRIPT'} = 'pre';
if ($args{'verbose'}) { print "executing pre_snapshot_script '".$config{$dataset}{'pre_snapshot_script'}."' on dataset '$dataset'\n"; }
if (!$args{'readonly'}) {
@ -567,7 +634,11 @@ sub take_snapshots {
}
delete $ENV{'SANOID_TARGET'};
delete $ENV{'SANOID_TARGETS'};
delete $ENV{'SANOID_SNAPNAME'};
delete $ENV{'SANOID_SNAPNAMES'};
delete $ENV{'SANOID_TYPES'};
delete $ENV{'SANOID_SCRIPT'};
if ($ret != 0) {
# warning was already thrown by runscript function
@ -575,47 +646,58 @@ sub take_snapshots {
$presnapshotfailure = 1;
}
}
if ($args{'verbose'}) { print "taking snapshot $snap$extraMessage\n"; }
if (!$args{'readonly'}) {
my $stderr;
my $exit;
($stderr, $exit) = tee_stderr {
if ($recursiveFlag) {
system($zfs, "snapshot", "-r", "$snap");
} else {
system($zfs, "snapshot", "$snap");
}
};
$exit == 0 or do {
if ($dstHandling) {
if ($stderr =~ /already exists/) {
$exit = 0;
$snap =~ s/_([a-z]+)$/dst_$1/g;
if ($args{'verbose'}) { print "taking dst snapshot $snap$extraMessage\n"; }
if ($recursiveFlag) {
system($zfs, "snapshot", "-r", "$snap") == 0
or warn "CRITICAL ERROR: $zfs snapshot -r $snap failed, $?";
} else {
system($zfs, "snapshot", "$snap") == 0
or warn "CRITICAL ERROR: $zfs snapshot $snap failed, $?";
foreach my $snap (@snapshots) {
$snap = "$dataset\@$snap";
if ($args{'verbose'}) { print "taking snapshot $snap$extraMessage\n"; }
if (!$args{'readonly'}) {
my $stderr;
my $exit;
($stderr, $exit) = tee_stderr {
if ($recursiveFlag) {
system($zfs, "snapshot", "-r", "$snap");
} else {
system($zfs, "snapshot", "$snap");
}
};
$exit == 0 or do {
if ($dstHandling) {
if ($stderr =~ /already exists/) {
$exit = 0;
$snap =~ s/_([a-z]+)$/dst_$1/g;
if ($args{'verbose'}) { print "taking dst snapshot $snap$extraMessage\n"; }
if ($recursiveFlag) {
system($zfs, "snapshot", "-r", "$snap") == 0
or warn "CRITICAL ERROR: $zfs snapshot -r $snap failed, $?";
} else {
system($zfs, "snapshot", "$snap") == 0
or warn "CRITICAL ERROR: $zfs snapshot $snap failed, $?";
}
}
}
}
};
};
$exit == 0 or do {
if ($recursiveFlag) {
warn "CRITICAL ERROR: $zfs snapshot -r $snap failed, $?";
} else {
warn "CRITICAL ERROR: $zfs snapshot $snap failed, $?";
}
};
$exit == 0 or do {
if ($recursiveFlag) {
warn "CRITICAL ERROR: $zfs snapshot -r $snap failed, $?";
} else {
warn "CRITICAL ERROR: $zfs snapshot $snap failed, $?";
}
};
}
}
if ($config{$dataset}{'post_snapshot_script'}) {
if (!$presnapshotfailure or $config{$dataset}{'force_post_snapshot_script'}) {
$ENV{'SANOID_TARGET'} = $dataset;
$ENV{'SANOID_SNAPNAME'} = $snapname;
$ENV{'SANOID_TARGETS'} = $datasetString;
$ENV{'SANOID_SNAPNAME'} = $snapshots[0];
$ENV{'SANOID_SNAPNAMES'} = $snapshotString;
$ENV{'SANOID_TYPES'} = $typeString;
$ENV{'SANOID_SCRIPT'} = 'post';
$ENV{'SANOID_PRE_FAILURE'} = $presnapshotfailure;
if ($args{'verbose'}) { print "executing post_snapshot_script '".$config{$dataset}{'post_snapshot_script'}."' on dataset '$dataset'\n"; }
if (!$args{'readonly'}) {
@ -623,7 +705,12 @@ sub take_snapshots {
}
delete $ENV{'SANOID_TARGET'};
delete $ENV{'SANOID_TARGETS'};
delete $ENV{'SANOID_SNAPNAME'};
delete $ENV{'SANOID_SNAPNAMES'};
delete $ENV{'SANOID_TYPES'};
delete $ENV{'SANOID_SCRIPT'};
delete $ENV{'SANOID_PRE_FAILURE'};
}
}
}
@ -771,7 +858,7 @@ sub getsnaps {
if (checklock('sanoid_cacheupdate')) {
writelock('sanoid_cacheupdate');
if ($args{'verbose'}) {
if ($args{'force-update'}) {
if ($forcecacheupdate) {
print "INFO: cache forcibly expired - updating from zfs list.\n";
} else {
print "INFO: cache expired - updating from zfs list.\n";
@ -781,9 +868,10 @@ sub getsnaps {
@rawsnaps = <FH>;
close FH;
open FH, "> $cache" or die 'Could not write to $cache!\n';
open FH, "> $cache.tmp" or die 'Could not write to $cache.tmp!\n';
print FH @rawsnaps;
close FH;
rename("$cache.tmp", "$cache") or die 'Could not rename to $cache!\n';
removelock('sanoid_cacheupdate');
} else {
if ($args{'verbose'}) { print "INFO: deferring cache update - valid cache update lock held by another sanoid process.\n"; }
@ -846,6 +934,20 @@ sub init {
die "FATAL: you're using sanoid.defaults.conf v$defaults_version, this version of sanoid requires a minimum sanoid.defaults.conf v$MINIMUM_DEFAULTS_VERSION";
}
my @updatedatasets;
# load dataset cache if valid
if (!$args{'force-update'} && -f $cachedatasetspath) {
my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($cachedatasetspath);
if ((time() - $mtime) <= $cacheTTL) {
if ($args{'debug'}) { print "DEBUG: dataset cache not expired (" . (time() - $mtime) . " seconds old with TTL of $cacheTTL): pulling dataset list from cache.\n"; }
open FH, "< $cachedatasetspath";
@cachedatasets = <FH>;
close FH;
}
}
foreach my $section (keys %ini) {
# first up - die with honor if unknown parameters are set in any modules or templates by the user.
@ -853,6 +955,15 @@ sub init {
if (! defined ($defaults{'template_default'}{$key})) {
die "FATAL ERROR: I don't understand the setting $key you've set in \[$section\] in $conf_file.\n";
}
# in case of duplicate lines we will end up with an array of all values
my $value = $ini{$section}{$key};
if (ref($value) eq 'ARRAY') {
warn "duplicate key '$key' in section '$section', using the value from the first occurence and ignoring the others.\n";
$ini{$section}{$key} = $value->[0];
}
# trim
$ini{$section}{$key} =~ s/^\s+|\s+$//g;
}
if ($section =~ /^template_/) { next; } # don't process templates directly
@ -861,7 +972,7 @@ sub init {
# for sections directly when they've already been defined recursively, without starting them over from scratch.
if (! defined ($config{$section}{'initialized'})) {
if ($args{'debug'}) { print "DEBUG: initializing \$config\{$section\} with default values from $default_conf_file.\n"; }
# set default values from %defaults, which can then be overriden by template
# set default values from %defaults, which can then be overridden by template
# and/or local settings within the module.
foreach my $key (keys %{$defaults{'template_default'}}) {
if (! ($key =~ /template|recursive|children_only/)) {
@ -926,6 +1037,10 @@ sub init {
$config{$section}{'path'} = $section;
}
if (! @cachedatasets) {
push (@updatedatasets, "$config{$section}{'path'}\n");
}
# how 'bout some recursion? =)
if ($config{$section}{'zfs_recursion'} && $config{$section}{'zfs_recursion'} == 1 && $config{$section}{'autosnap'} == 1) {
warn "ignored autosnap configuration for '$section' because it's part of a zfs recursion.\n";
@ -943,6 +1058,10 @@ sub init {
@datasets = getchilddatasets($config{$section}{'path'});
DATASETS: foreach my $dataset(@datasets) {
if (! @cachedatasets) {
push (@updatedatasets, $dataset);
}
chomp $dataset;
if ($zfsRecursive) {
@ -974,9 +1093,27 @@ sub init {
$config{$dataset}{'initialized'} = 1;
}
}
}
# update dataset cache if it was unused
if (! @cachedatasets) {
if (checklock('sanoid_cachedatasetupdate')) {
writelock('sanoid_cachedatasetupdate');
if ($args{'verbose'}) {
if ($args{'force-update'}) {
print "INFO: dataset cache forcibly expired - updating from zfs list.\n";
} else {
print "INFO: dataset cache expired - updating from zfs list.\n";
}
}
open FH, "> $cachedatasetspath.tmp" or die 'Could not write to $cachedatasetspath.tmp!\n';
print FH @updatedatasets;
close FH;
rename("$cachedatasetspath.tmp", "$cachedatasetspath") or die 'Could not rename to $cachedatasetspath!\n';
removelock('sanoid_cachedatasetupdate');
} else {
if ($args{'verbose'}) { print "INFO: deferring dataset cache update - valid cache update lock held by another sanoid process.\n"; }
}
}
return %config;
@ -1109,7 +1246,7 @@ sub check_zpool() {
}
}
# Tony: Debuging
# Tony: Debugging
# print "Size: $size \t Used: $used \t Avai: $avail \t Cap: $cap \t Health: $health\n";
close(STAT);
@ -1211,7 +1348,7 @@ sub check_zpool() {
## no display for verbose level 1
next if ($verbose==1);
## don't display working devices for verbose level 2
if ($verbose==2 && ($state eq "OK" || $sta eq "ONLINE" || $sta eq "AVAIL" || $sta eq "INUSE")) {
if ($verbose==2 && ($state eq "OK" || $sta eq "ONLINE" || $sta eq "AVAIL")) {
# check for io/checksum errors
my @vdeverr = ();
@ -1526,6 +1663,30 @@ sub getchilddatasets {
my $fs = shift;
my $mysudocmd = '';
# use dataset cache if available
if (@cachedatasets) {
my $foundparent = 0;
my @cachechildren = ();
foreach my $dataset (@cachedatasets) {
chomp $dataset;
my $ret = rindex $dataset, "${fs}/", 0;
if ($ret == 0) {
push (@cachechildren, $dataset);
} else {
if ($dataset eq $fs) {
$foundparent = 1;
}
}
}
# sanity check
if ($foundparent) {
return @cachechildren;
}
# fallback if cache misses items for whatever reason
}
my $getchildrencmd = "$mysudocmd $zfs list -o name -t filesystem,volume -Hr $fs |";
if ($args{'debug'}) { print "DEBUG: getting list of child datasets on $fs using $getchildrencmd...\n"; }
open FH, $getchildrencmd;
@ -1572,16 +1733,17 @@ sub removecachedsnapshots {
my @rawsnaps = <FH>;
close FH;
open FH, "> $cache" or die 'Could not write to $cache!\n';
open FH, "> $cache.tmp" or die 'Could not write to $cache.tmp!\n';
foreach my $snapline ( @rawsnaps ) {
my @columns = split("\t", $snapline);
my $snap = $columns[0];
print FH $snapline unless ( exists($pruned{$snap}) );
}
close FH;
rename("$cache.tmp", "$cache") or die 'Could not rename to $cache!\n';
removelock('sanoid_cacheupdate');
%snaps = getsnaps(\%config,$cacheTTL,$forcecacheupdate);
%snaps = getsnaps(\%config,$cacheTTL,0);
# clear hash
undef %pruned;

View File

@ -3,30 +3,39 @@
# It should go in /etc/sanoid. #
######################################
# name your backup modules with the path to their ZFS dataset - no leading slash.
[zpoolname/datasetname]
# pick one or more templates - they're defined (and editable) below. Comma separated, processed in order.
# in this example, template_demo's daily value overrides template_production's daily value.
use_template = production,demo
## name your backup modules with the path to their ZFS dataset - no leading slash.
#[zpoolname/datasetname]
# # pick one or more templates - they're defined (and editable) below. Comma separated, processed in order.
# # in this example, template_demo's daily value overrides template_production's daily value.
# use_template = production,demo
#
# # if you want to, you can override settings in the template directly inside module definitions like this.
# # in this example, we override the template to only keep 12 hourly and 1 monthly snapshot for this dataset.
# hourly = 12
# monthly = 1
#
## you can also handle datasets recursively.
#[zpoolname/parent]
# use_template = production
# recursive = yes
# # if you want sanoid to manage the child datasets but leave this one alone, set process_children_only.
# process_children_only = yes
#
## you can selectively override settings for child datasets which already fall under a recursive definition.
#[zpoolname/parent/child]
# # child datasets already initialized won't be wiped out, so if you use a new template, it will
# # only override the values already set by the parent template, not replace it completely.
# use_template = demo
# if you want to, you can override settings in the template directly inside module definitions like this.
# in this example, we override the template to only keep 12 hourly and 1 monthly snapshot for this dataset.
hourly = 12
monthly = 1
# you can also handle datasets recursively.
[zpoolname/parent]
# you can also handle datasets recursively in an atomic way without the possibility to override settings for child datasets.
[zpoolname/parent2]
use_template = production
recursive = yes
# if you want sanoid to manage the child datasets but leave this one alone, set process_children_only.
process_children_only = yes
# you can selectively override settings for child datasets which already fall under a recursive definition.
[zpoolname/parent/child]
# child datasets already initialized won't be wiped out, so if you use a new template, it will
# only override the values already set by the parent template, not replace it completely.
use_template = demo
# there are two options for recursive: zfs or yes
# * zfs - taken a zfs snapshot with the '-r' flag; zfs will recursively take a snapshot of the whole
# dataset tree which is consistent.
# * yes - the snapshots will be taken one-at-time through the sanoid code; not necessarily consistent.
recursive = zfs
@ -91,12 +100,14 @@
daily_crit = 4d
[template_scripts]
### dataset and snapshot name will be supplied as environment variables
### for all pre/post/prune scripts ($SANOID_TARGET, $SANOID_SNAPNAME)
### information about the snapshot will be supplied as environment variables,
### see the README.md file for details about what is passed when.
### run script before snapshot
pre_snapshot_script = /path/to/script.sh
### run script after snapshot
post_snapshot_script = /path/to/script.sh
### run script before pruning snapshot
pre_pruning_script = /path/to/script.sh
### run script after pruning snapshot
pruning_script = /path/to/script.sh
### don't take an inconsistent snapshot (skip if pre script fails)

View File

@ -19,8 +19,10 @@ use_template =
process_children_only =
skip_children =
# See "Sanoid script hooks" in README.md for information about scripts.
pre_snapshot_script =
post_snapshot_script =
pre_pruning_script =
pruning_script =
script_timeout = 5
no_inconsistent_snapshot =
@ -108,5 +110,6 @@ yearly_warn = 0
yearly_crit = 0
# default limits for capacity checks (if set to 0, limit will not be checked)
# for overriding these values one needs to specify them in a root pool section! ([tank]\n ...)
capacity_warn = 80
capacity_crit = 95

1487
syncoid

File diff suppressed because it is too large Load Diff

View File

@ -34,7 +34,7 @@ function checkEnvironment {
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo "you should be running this test in a"
echo "dedicated vm, as it will mess with your system!"
echo "Are you sure you wan't to continue? (y)"
echo "Are you sure you want to continue? (y)"
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
set -x

View File

@ -2,7 +2,7 @@
# run's all the available tests
for test in */; do
for test in $(find . -mindepth 1 -maxdepth 1 -type d -printf "%P\n" | sort -g); do
if [ ! -x "${test}/run.sh" ]; then
continue
fi
@ -17,8 +17,11 @@ for test in */; do
cd "${test}"
echo -n y | bash run.sh > "${LOGFILE}" 2>&1
if [ $? -eq 0 ]; then
ret=$?
if [ $ret -eq 0 ]; then
echo "[PASS]"
elif [ $ret -eq 130 ]; then
echo "[SKIPPED]"
else
echo "[FAILED] (see ${LOGFILE})"
fi

View File

@ -28,6 +28,8 @@ zfs create -o mountpoint="${MOUNT_TARGET}" "${POOL_NAME}"/src
dd if=/dev/urandom of="${MOUNT_TARGET}"/big_file bs=1M count=200
sleep 1
../../../syncoid --debug --compress=none --source-bwlimit=2m "${POOL_NAME}"/src "${POOL_NAME}"/dst &
syncoid_pid=$!
sleep 5
@ -45,6 +47,9 @@ wait
sleep 1
../../../syncoid --debug --compress=none --no-resume "${POOL_NAME}"/src "${POOL_NAME}"/dst | grep "reset partial receive state of syncoid"
sleep 1
../../../syncoid --debug --compress=none "${POOL_NAME}"/src "${POOL_NAME}"/dst
exit $?

View File

@ -28,6 +28,8 @@ zfs create -o mountpoint="${MOUNT_TARGET}" "${POOL_NAME}"/src
dd if=/dev/urandom of="${MOUNT_TARGET}"/big_file bs=1M count=200
sleep 1
zfs snapshot "${POOL_NAME}"/src@big
../../../syncoid --debug --no-sync-snap --compress=none --source-bwlimit=2m "${POOL_NAME}"/src "${POOL_NAME}"/dst &
syncoid_pid=$!
@ -47,6 +49,9 @@ sleep 1
zfs destroy "${POOL_NAME}"/src@big
../../../syncoid --debug --compress=none "${POOL_NAME}"/src "${POOL_NAME}"/dst # | grep "reset partial receive state of syncoid"
sleep 1
../../../syncoid --debug --compress=none "${POOL_NAME}"/src "${POOL_NAME}"/dst
exit $?

View File

@ -0,0 +1,48 @@
#!/bin/bash
# test preserving the recordsize from the src filesystem to the target one
set -x
set -e
. ../../common/lib.sh
POOL_IMAGE="/tmp/syncoid-test-7.zpool"
MOUNT_TARGET="/tmp/syncoid-test-7.mount"
POOL_SIZE="1000M"
POOL_NAME="syncoid-test-7"
truncate -s "${POOL_SIZE}" "${POOL_IMAGE}"
zpool create -m none -f "${POOL_NAME}" "${POOL_IMAGE}"
function cleanUp {
zpool export "${POOL_NAME}"
}
# export pool in any case
trap cleanUp EXIT
zfs create "${POOL_NAME}"/src
zfs create -V 100M -o volblocksize=4k "${POOL_NAME}"/src/zvol4
zfs create -V 100M -o volblocksize=16k "${POOL_NAME}"/src/zvol16
zfs create -V 100M -o volblocksize=64k "${POOL_NAME}"/src/zvol64
zfs create -o recordsize=16k "${POOL_NAME}"/src/16
zfs create -o recordsize=32k "${POOL_NAME}"/src/32
zfs create -o recordsize=128k "${POOL_NAME}"/src/128
../../../syncoid --preserve-recordsize --recursive --debug --compress=none "${POOL_NAME}"/src "${POOL_NAME}"/dst
zfs get -t filesystem -r recordsize "${POOL_NAME}"/dst
zfs get -t volume -r volblocksize "${POOL_NAME}"/dst
if [ "$(zfs get -H -o value -t filesystem recordsize "${POOL_NAME}"/dst/16)" != "16K" ]; then
exit 1
fi
if [ "$(zfs get -H -o value -t filesystem recordsize "${POOL_NAME}"/dst/32)" != "32K" ]; then
exit 1
fi
if [ "$(zfs get -H -o value -t filesystem recordsize "${POOL_NAME}"/dst/128)" != "128K" ]; then
exit 1
fi

View File

@ -0,0 +1,48 @@
#!/bin/bash
# test replication with deletion of conflicting snapshot on target
set -x
set -e
. ../../common/lib.sh
POOL_IMAGE="/tmp/syncoid-test-8.zpool"
POOL_SIZE="200M"
POOL_NAME="syncoid-test-8"
TARGET_CHECKSUM="ee439200c9fa54fc33ce301ef64d4240a6c5587766bfeb651c5cf358e11ec89d -"
truncate -s "${POOL_SIZE}" "${POOL_IMAGE}"
zpool create -m none -f "${POOL_NAME}" "${POOL_IMAGE}"
function cleanUp {
zpool export "${POOL_NAME}"
}
# export pool in any case
trap cleanUp EXIT
zfs create "${POOL_NAME}"/src
zfs snapshot "${POOL_NAME}"/src@duplicate
# initial replication
../../../syncoid -r --debug --compress=none "${POOL_NAME}"/src "${POOL_NAME}"/dst
# recreate snapshot with the same name on src
zfs destroy "${POOL_NAME}"/src@duplicate
zfs snapshot "${POOL_NAME}"/src@duplicate
sleep 1
../../../syncoid -r --force-delete --debug --compress=none "${POOL_NAME}"/src "${POOL_NAME}"/dst || exit 1
# verify
output1=$(zfs list -t snapshot -r -H -o guid,name "${POOL_NAME}"/src | sed 's/@syncoid_.*$'/@syncoid_/)
checksum1=$(echo "${output1}" | shasum -a 256)
output2=$(zfs list -t snapshot -r -H -o guid,name "${POOL_NAME}"/dst | sed 's/@syncoid_.*$'/@syncoid_/ | sed 's/dst/src/')
checksum2=$(echo "${output2}" | shasum -a 256)
if [ "${checksum1}" != "${checksum2}" ]; then
exit 1
fi
exit 0

View File

@ -0,0 +1,71 @@
#!/bin/bash
# test preserving locally set properties from the src dataset to the target one
set -x
set -e
. ../../common/lib.sh
POOL_IMAGE="/tmp/syncoid-test-9.zpool"
MOUNT_TARGET="/tmp/syncoid-test-9.mount"
POOL_SIZE="1000M"
POOL_NAME="syncoid-test-9"
truncate -s "${POOL_SIZE}" "${POOL_IMAGE}"
zpool create -m none -f "${POOL_NAME}" "${POOL_IMAGE}"
function cleanUp {
zpool export "${POOL_NAME}"
}
# export pool in any case
trap cleanUp EXIT
zfs create -o recordsize=16k -o xattr=on -o mountpoint=none -o primarycache=none "${POOL_NAME}"/src
zfs create -V 100M -o volblocksize=8k "${POOL_NAME}"/src/zvol8
zfs create -V 100M -o volblocksize=16k -o primarycache=all "${POOL_NAME}"/src/zvol16
zfs create -V 100M -o volblocksize=64k "${POOL_NAME}"/src/zvol64
zfs create -o recordsize=16k -o primarycache=none "${POOL_NAME}"/src/16
zfs create -o recordsize=32k -o acltype=posixacl "${POOL_NAME}"/src/32
zfs set 'net.openoid:var-name'='with whitespace and !"§$%&/()= symbols' "${POOL_NAME}"/src/32
../../../syncoid --preserve-properties --recursive --debug --compress=none "${POOL_NAME}"/src "${POOL_NAME}"/dst
if [ "$(zfs get -H -o value -t filesystem recordsize "${POOL_NAME}"/dst)" != "16K" ]; then
exit 1
fi
if [ "$(zfs get -H -o value -t filesystem mountpoint "${POOL_NAME}"/dst)" != "none" ]; then
exit 1
fi
if [ "$(zfs get -H -o value -t filesystem xattr "${POOL_NAME}"/dst)" != "on" ]; then
exit 1
fi
if [ "$(zfs get -H -o value -t filesystem primarycache "${POOL_NAME}"/dst)" != "none" ]; then
exit 1
fi
if [ "$(zfs get -H -o value -t filesystem recordsize "${POOL_NAME}"/dst/16)" != "16K" ]; then
exit 1
fi
if [ "$(zfs get -H -o value -t filesystem primarycache "${POOL_NAME}"/dst/16)" != "none" ]; then
exit 1
fi
if [ "$(zfs get -H -o value -t filesystem recordsize "${POOL_NAME}"/dst/32)" != "32K" ]; then
exit 1
fi
if [ "$(zfs get -H -o value -t filesystem acltype "${POOL_NAME}"/dst/32)" != "posix" ]; then
exit 1
fi
if [ "$(zfs get -H -o value -t filesystem 'net.openoid:var-name' "${POOL_NAME}"/dst/32)" != "with whitespace and !\"§$%&/()= symbols" ]; then
exit 1
fi

View File

@ -0,0 +1,142 @@
#!/bin/bash
# test filtering snapshot names using --include-snaps and --exclude-snaps
set -x
set -e
. ../../common/lib.sh
POOL_IMAGE="/tmp/syncoid-test-10.zpool"
MOUNT_TARGET="/tmp/syncoid-test-10.mount"
POOL_SIZE="100M"
POOL_NAME="syncoid-test-10"
truncate -s "${POOL_SIZE}" "${POOL_IMAGE}"
zpool create -m none -f "${POOL_NAME}" "${POOL_IMAGE}"
#####
# Create source snapshots and destroy the destination snaps and dataset.
#####
function setup_snaps {
# create intermediate snapshots
# sleep is needed so creation time can be used for proper sorting
sleep 1
zfs snapshot "${POOL_NAME}"/src@monthly1
sleep 1
zfs snapshot "${POOL_NAME}"/src@daily1
sleep 1
zfs snapshot "${POOL_NAME}"/src@daily2
sleep 1
zfs snapshot "${POOL_NAME}"/src@hourly1
sleep 1
zfs snapshot "${POOL_NAME}"/src@hourly2
sleep 1
zfs snapshot "${POOL_NAME}"/src@daily3
sleep 1
zfs snapshot "${POOL_NAME}"/src@hourly3
sleep 1
zfs snapshot "${POOL_NAME}"/src@hourly4
}
#####
# Remove the destination snapshots and dataset so that each test starts with a
# blank slate.
#####
function clean_snaps {
zfs destroy "${POOL_NAME}"/dst@%
zfs destroy "${POOL_NAME}"/dst
}
#####
# Verify that the correct set of snapshots is present on the destination.
#####
function verify_checksum {
zfs list -r -t snap "${POOL_NAME}"
checksum=$(zfs list -t snap -r -H -o name "${POOL_NAME}" | sed 's/@syncoid_.*/@syncoid_/' | shasum -a 256)
echo "Expected checksum: $1"
echo "Actual checksum: $checksum"
return $( [[ "$checksum" == "$1" ]] )
}
function cleanUp {
zpool export "${POOL_NAME}"
}
# export pool in any case
trap cleanUp EXIT
zfs create "${POOL_NAME}"/src
setup_snaps
#####
# TEST 1
#
# --exclude-snaps is provided and --no-stream is omitted. Hourly snaps should
# be missing from the destination, and all other intermediate snaps should be
# present.
#####
../../../syncoid --debug --compress=none --no-sync-snap --exclude-snaps='hourly' "${POOL_NAME}"/src "${POOL_NAME}"/dst
verify_checksum '494b6860415607f1d670e4106a10e1316924ba6cd31b4ddacffe0ad6d30a6339 -'
clean_snaps
#####
# TEST 2
#
# --exclude-snaps and --no-stream are provided. Only the daily3 snap should be
# present on the destination.
#####
../../../syncoid --debug --compress=none --no-sync-snap --exclude-snaps='hourly' --no-stream "${POOL_NAME}"/src "${POOL_NAME}"/dst
verify_checksum '0a5072f42180d231cfdd678682972fbbb689140b7f3e996b3c348b7e78d67ea2 -'
clean_snaps
#####
# TEST 3
#
# --include-snaps is provided and --no-stream is omitted. Hourly snaps should
# be present on the destination, and all other snaps should be missing
#####
../../../syncoid --debug --compress=none --no-sync-snap --include-snaps='hourly' "${POOL_NAME}"/src "${POOL_NAME}"/dst
verify_checksum 'd32862be4c71c6cde846322a7d006fd5e8edbd3520d3c7b73953492946debb7f -'
clean_snaps
#####
# TEST 4
#
# --include-snaps and --no-stream are provided. Only the hourly4 snap should
# be present on the destination.
#####
../../../syncoid --debug --compress=none --no-sync-snap --include-snaps='hourly' --no-stream "${POOL_NAME}"/src "${POOL_NAME}"/dst
verify_checksum '81ef1a8298006a7ed856430bb7e05e8b85bbff530ca9dd7831f1da782f8aa4c7 -'
clean_snaps
#####
# TEST 5
#
# --include-snaps='hourly' and --exclude-snaps='3' are both provided. The
# hourly snaps should be present on the destination except for hourly3; daily
# and monthly snaps should be missing.
#####
../../../syncoid --debug --compress=none --no-sync-snap --include-snaps='hourly' --exclude-snaps='3' "${POOL_NAME}"/src "${POOL_NAME}"/dst
verify_checksum '5a9dd92b7d4b8760a1fcad03be843da4f43b915c64caffc1700c0d59a1581239 -'
clean_snaps
#####
# TEST 6
#
# --exclude-snaps='syncoid' and --no-stream are provided, and --no-sync-snap is
# omitted. The sync snap should be created on the source but not sent to the
# destination; only hourly4 should be sent.
#####
../../../syncoid --debug --compress=none --no-stream --exclude-snaps='syncoid' "${POOL_NAME}"/src "${POOL_NAME}"/dst
verify_checksum '9394fdac44ec72764a4673202552599684c83530a2a724dae5b411aaea082b02 -'
clean_snaps

View File

@ -0,0 +1,50 @@
#!/bin/bash
# test verifying snapshots with out-of-order snapshot creation datetimes
set -x
set -e
. ../../common/lib.sh
if [ -z "$ALLOW_INVASIVE_TESTS" ]; then
exit 130
fi
POOL_IMAGE="/tmp/syncoid-test-11.zpool"
POOL_SIZE="64M"
POOL_NAME="syncoid-test-11"
truncate -s "${POOL_SIZE}" "${POOL_IMAGE}"
zpool create -m none -f "${POOL_NAME}" "${POOL_IMAGE}"
function cleanUp {
zpool export "${POOL_NAME}"
rm -f "${POOL_IMAGE}"
}
# export pool and remove the image in any case
trap cleanUp EXIT
zfs create "${POOL_NAME}"/before
zfs snapshot "${POOL_NAME}"/before@this-snapshot-should-make-it-into-the-after-dataset
disableTimeSync
setdate 1155533696
zfs snapshot "${POOL_NAME}"/before@oldest-snapshot
zfs snapshot "${POOL_NAME}"/before@another-snapshot-does-not-matter
../../../syncoid --sendoptions="Lec" "${POOL_NAME}"/before "${POOL_NAME}"/after
# verify
saveSnapshotList "${POOL_NAME}" "snapshot-list.txt"
grep "${POOL_NAME}/before@this-snapshot-should-make-it-into-the-after-dataset" "snapshot-list.txt" || exit $?
grep "${POOL_NAME}/after@this-snapshot-should-make-it-into-the-after-dataset" "snapshot-list.txt" || exit $?
grep "${POOL_NAME}/before@oldest-snapshot" "snapshot-list.txt" || exit $?
grep "${POOL_NAME}/after@oldest-snapshot" "snapshot-list.txt" || exit $?
grep "${POOL_NAME}/before@another-snapshot-does-not-matter" "snapshot-list.txt" || exit $?
grep "${POOL_NAME}/after@another-snapshot-does-not-matter" "snapshot-list.txt" || exit $?
exit 0

View File

@ -2,7 +2,7 @@
# run's all the available tests
for test in */; do
for test in $(find . -mindepth 1 -maxdepth 1 -type d -printf "%P\n" | sort -g); do
if [ ! -x "${test}/run.sh" ]; then
continue
fi
@ -17,8 +17,11 @@ for test in */; do
cd "${test}"
echo | bash run.sh > "${LOGFILE}" 2>&1
if [ $? -eq 0 ]; then
ret=$?
if [ $ret -eq 0 ]; then
echo "[PASS]"
elif [ $ret -eq 130 ]; then
echo "[SKIPPED]"
else
echo "[FAILED] (see ${LOGFILE})"
fi