forked from third-party-mirrors/zfs_autobackup
pre/post cmd tests
This commit is contained in:
parent
119225ba5b
commit
c5363a1538
@ -419,7 +419,9 @@ decompression -> custom recv pipes -> buffer -> zfs recv
|
|||||||
|
|
||||||
## Running custom commands before and after snapshotting
|
## Running custom commands before and after snapshotting
|
||||||
|
|
||||||
If you need, e.g. to quiesce a couple of mysql databases to make on-disk data consistent before snapshotting, you can use the `--pre-snapshot-cmd` and `--post-snapshot-cmd` options.
|
You can run commands before and after the snapshot to freeze databases to make the on for example to make the on-disk data consistent before snapshotting.
|
||||||
|
|
||||||
|
The commands will be executed on the source side. Use the `--pre-snapshot-cmd` and `--post-snapshot-cmd` options for this.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
@ -432,9 +434,10 @@ zfs-autobackup \
|
|||||||
backupfs1
|
backupfs1
|
||||||
```
|
```
|
||||||
|
|
||||||
The post-snapshot commands are ALWAYS executed, even if a pre-snapshot command or the actual snapshot fail.
|
Failure handling during pre/post commands:
|
||||||
|
|
||||||
A failure of a post-snapshot command is non-fatal and will be ignored. The remaining post-snapshot commands wont be executed in that case.
|
* If a pre-command fails, zfs-autobackup will exit with an error. (after executing the post-commands)
|
||||||
|
* All post-commands are always executed. Even if the pre-commands or actual snapshot have failed. This way you can be sure that stuff is always cleanedup and unfreezed.
|
||||||
|
|
||||||
## Tips
|
## Tips
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from basetest import *
|
from basetest import *
|
||||||
from zfs_autobackup.LogStub import LogStub
|
from zfs_autobackup.LogStub import LogStub
|
||||||
|
from zfs_autobackup.ExecuteNode import ExecuteError
|
||||||
|
|
||||||
|
|
||||||
class TestZfsNode(unittest2.TestCase):
|
class TestZfsNode(unittest2.TestCase):
|
||||||
@ -9,16 +9,15 @@ class TestZfsNode(unittest2.TestCase):
|
|||||||
prepare_zpools()
|
prepare_zpools()
|
||||||
# return super().setUp()
|
# return super().setUp()
|
||||||
|
|
||||||
|
|
||||||
def test_consistent_snapshot(self):
|
def test_consistent_snapshot(self):
|
||||||
logger=LogStub()
|
logger = LogStub()
|
||||||
description="[Source]"
|
description = "[Source]"
|
||||||
node=ZfsNode("test", logger, description=description)
|
node = ZfsNode("test", logger, description=description)
|
||||||
|
|
||||||
with self.subTest("first snapshot"):
|
with self.subTest("first snapshot"):
|
||||||
node.consistent_snapshot(node.selected_datasets(exclude_paths=[], exclude_received=False), "test-1",100000)
|
node.consistent_snapshot(node.selected_datasets(exclude_paths=[], exclude_received=False), "test-1", 100000)
|
||||||
r=shelltest("zfs list -H -o name -r -t all "+TEST_POOLS)
|
r = shelltest("zfs list -H -o name -r -t all " + TEST_POOLS)
|
||||||
self.assertEqual(r,"""
|
self.assertEqual(r, """
|
||||||
test_source1
|
test_source1
|
||||||
test_source1/fs1
|
test_source1/fs1
|
||||||
test_source1/fs1@test-1
|
test_source1/fs1@test-1
|
||||||
@ -33,11 +32,10 @@ test_source2/fs3/sub
|
|||||||
test_target1
|
test_target1
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
|
||||||
with self.subTest("second snapshot, no changes, no snapshot"):
|
with self.subTest("second snapshot, no changes, no snapshot"):
|
||||||
node.consistent_snapshot(node.selected_datasets(exclude_paths=[], exclude_received=False), "test-2",1)
|
node.consistent_snapshot(node.selected_datasets(exclude_paths=[], exclude_received=False), "test-2", 1)
|
||||||
r=shelltest("zfs list -H -o name -r -t all "+TEST_POOLS)
|
r = shelltest("zfs list -H -o name -r -t all " + TEST_POOLS)
|
||||||
self.assertEqual(r,"""
|
self.assertEqual(r, """
|
||||||
test_source1
|
test_source1
|
||||||
test_source1/fs1
|
test_source1/fs1
|
||||||
test_source1/fs1@test-1
|
test_source1/fs1@test-1
|
||||||
@ -53,9 +51,9 @@ test_target1
|
|||||||
""")
|
""")
|
||||||
|
|
||||||
with self.subTest("second snapshot, no changes, empty snapshot"):
|
with self.subTest("second snapshot, no changes, empty snapshot"):
|
||||||
node.consistent_snapshot(node.selected_datasets(exclude_paths=[], exclude_received=False), "test-2",0)
|
node.consistent_snapshot(node.selected_datasets(exclude_paths=[], exclude_received=False), "test-2", 0)
|
||||||
r=shelltest("zfs list -H -o name -r -t all "+TEST_POOLS)
|
r = shelltest("zfs list -H -o name -r -t all " + TEST_POOLS)
|
||||||
self.assertEqual(r,"""
|
self.assertEqual(r, """
|
||||||
test_source1
|
test_source1
|
||||||
test_source1/fs1
|
test_source1/fs1
|
||||||
test_source1/fs1@test-1
|
test_source1/fs1@test-1
|
||||||
@ -73,31 +71,82 @@ test_source2/fs3/sub
|
|||||||
test_target1
|
test_target1
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
def test_consistent_snapshot_prepostcmds(self):
|
||||||
|
logger = LogStub()
|
||||||
|
description = "[Source]"
|
||||||
|
node = ZfsNode("test", logger, description=description, debug_output=True)
|
||||||
|
|
||||||
|
with self.subTest("Test if all cmds are executed correctly (no failures)"):
|
||||||
|
with OutputIO() as buf:
|
||||||
|
with redirect_stdout(buf):
|
||||||
|
node.consistent_snapshot(node.selected_datasets(exclude_paths=[], exclude_received=False), "test-1",
|
||||||
|
0,
|
||||||
|
pre_snapshot_cmds=["echo pre1", "echo pre2"],
|
||||||
|
post_snapshot_cmds=["echo post1 >&2", "echo post2 >&2"]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIn("STDOUT > pre1", buf.getvalue())
|
||||||
|
self.assertIn("STDOUT > pre2", buf.getvalue())
|
||||||
|
self.assertIn("STDOUT > post1", buf.getvalue())
|
||||||
|
self.assertIn("STDOUT > post2", buf.getvalue())
|
||||||
|
|
||||||
|
|
||||||
|
with self.subTest("Failure in the middle, only pre1 and both post1 and post2 should be executed, no snapshot should be attempted"):
|
||||||
|
with OutputIO() as buf:
|
||||||
|
with redirect_stdout(buf):
|
||||||
|
with self.assertRaises(ExecuteError):
|
||||||
|
node.consistent_snapshot(node.selected_datasets(exclude_paths=[], exclude_received=False), "test-1",
|
||||||
|
0,
|
||||||
|
pre_snapshot_cmds=["echo pre1", "false", "echo pre2"],
|
||||||
|
post_snapshot_cmds=["echo post1", "false", "echo post2"]
|
||||||
|
)
|
||||||
|
|
||||||
|
print(buf.getvalue())
|
||||||
|
self.assertIn("STDOUT > pre1", buf.getvalue())
|
||||||
|
self.assertNotIn("STDOUT > pre2", buf.getvalue())
|
||||||
|
self.assertIn("STDOUT > post1", buf.getvalue())
|
||||||
|
self.assertIn("STDOUT > post2", buf.getvalue())
|
||||||
|
|
||||||
|
with self.subTest("Snapshot fails"):
|
||||||
|
with OutputIO() as buf:
|
||||||
|
with redirect_stdout(buf):
|
||||||
|
with self.assertRaises(ExecuteError):
|
||||||
|
#same snapshot name as before so it fails
|
||||||
|
node.consistent_snapshot(node.selected_datasets(exclude_paths=[], exclude_received=False), "test-1",
|
||||||
|
0,
|
||||||
|
pre_snapshot_cmds=["echo pre1", "echo pre2"],
|
||||||
|
post_snapshot_cmds=["echo post1", "echo post2"]
|
||||||
|
)
|
||||||
|
|
||||||
|
print(buf.getvalue())
|
||||||
|
self.assertIn("STDOUT > pre1", buf.getvalue())
|
||||||
|
self.assertIn("STDOUT > pre2", buf.getvalue())
|
||||||
|
self.assertIn("STDOUT > post1", buf.getvalue())
|
||||||
|
self.assertIn("STDOUT > post2", buf.getvalue())
|
||||||
|
|
||||||
|
|
||||||
def test_getselected(self):
|
def test_getselected(self):
|
||||||
logger=LogStub()
|
logger = LogStub()
|
||||||
description="[Source]"
|
description = "[Source]"
|
||||||
node=ZfsNode("test", logger, description=description)
|
node = ZfsNode("test", logger, description=description)
|
||||||
s=pformat(node.selected_datasets(exclude_paths=[], exclude_received=False))
|
s = pformat(node.selected_datasets(exclude_paths=[], exclude_received=False))
|
||||||
print(s)
|
print(s)
|
||||||
|
|
||||||
#basics
|
# basics
|
||||||
self.assertEqual (s, """[(local): test_source1/fs1,
|
self.assertEqual(s, """[(local): test_source1/fs1,
|
||||||
(local): test_source1/fs1/sub,
|
(local): test_source1/fs1/sub,
|
||||||
(local): test_source2/fs2/sub]""")
|
(local): test_source2/fs2/sub]""")
|
||||||
|
|
||||||
#caching, so expect same result after changing it
|
# caching, so expect same result after changing it
|
||||||
subprocess.check_call("zfs set autobackup:test=true test_source2/fs3", shell=True)
|
subprocess.check_call("zfs set autobackup:test=true test_source2/fs3", shell=True)
|
||||||
self.assertEqual (s, """[(local): test_source1/fs1,
|
self.assertEqual(s, """[(local): test_source1/fs1,
|
||||||
(local): test_source1/fs1/sub,
|
(local): test_source1/fs1/sub,
|
||||||
(local): test_source2/fs2/sub]""")
|
(local): test_source2/fs2/sub]""")
|
||||||
|
|
||||||
|
|
||||||
def test_validcommand(self):
|
def test_validcommand(self):
|
||||||
logger=LogStub()
|
logger = LogStub()
|
||||||
description="[Source]"
|
description = "[Source]"
|
||||||
node=ZfsNode("test", logger, description=description)
|
node = ZfsNode("test", logger, description=description)
|
||||||
|
|
||||||
|
|
||||||
with self.subTest("test invalid option"):
|
with self.subTest("test invalid option"):
|
||||||
self.assertFalse(node.valid_command(["zfs", "send", "--invalid-option", "nonexisting"]))
|
self.assertFalse(node.valid_command(["zfs", "send", "--invalid-option", "nonexisting"]))
|
||||||
@ -105,21 +154,19 @@ test_target1
|
|||||||
self.assertTrue(node.valid_command(["zfs", "send", "-v", "nonexisting"]))
|
self.assertTrue(node.valid_command(["zfs", "send", "-v", "nonexisting"]))
|
||||||
|
|
||||||
def test_supportedsendoptions(self):
|
def test_supportedsendoptions(self):
|
||||||
logger=LogStub()
|
logger = LogStub()
|
||||||
description="[Source]"
|
description = "[Source]"
|
||||||
node=ZfsNode("test", logger, description=description)
|
node = ZfsNode("test", logger, description=description)
|
||||||
# -D propably always supported
|
# -D propably always supported
|
||||||
self.assertGreater(len(node.supported_send_options),0)
|
self.assertGreater(len(node.supported_send_options), 0)
|
||||||
|
|
||||||
|
|
||||||
def test_supportedrecvoptions(self):
|
def test_supportedrecvoptions(self):
|
||||||
logger=LogStub()
|
logger = LogStub()
|
||||||
description="[Source]"
|
description = "[Source]"
|
||||||
#NOTE: this could hang via ssh if we dont close filehandles properly. (which was a previous bug)
|
# NOTE: this could hang via ssh if we dont close filehandles properly. (which was a previous bug)
|
||||||
node=ZfsNode("test", logger, description=description, ssh_to='localhost')
|
node = ZfsNode("test", logger, description=description, ssh_to='localhost')
|
||||||
self.assertIsInstance(node.supported_recv_options, list)
|
self.assertIsInstance(node.supported_recv_options, list)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -213,7 +213,6 @@ class ZfsNode(ExecuteNode):
|
|||||||
self.run(cmd=shlex.split(cmd), readonly=False)
|
self.run(cmd=shlex.split(cmd), readonly=False)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
pass
|
pass
|
||||||
self.warning("Post snapshot command failed, ignoring.")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user