Compare commits

..

6 Commits

Author SHA1 Message Date
628cd75941 fix 2021-02-07 21:17:40 +01:00
1da14c5c3b forgot to document child 2021-02-07 21:09:03 +01:00
c83d0fcff2 explain inheritance 2021-02-07 20:59:19 +01:00
573af341b8 fixes 2021-02-07 18:04:00 +01:00
a64168bee2 fixes 2021-02-07 18:00:58 +01:00
c678ae5f9a fixed --ignore-transfer-errors 2021-02-07 16:57:12 +01:00
5 changed files with 55 additions and 20 deletions

View File

@ -148,6 +148,8 @@ rpool/swap autobackup:offsite1 true
... ...
``` ```
ZFS properties are ```inherited``` by child datasets. Since we've set the property on the highest dataset, we're essentially backupping the whole pool.
Because we don't want to backup everything, we can exclude certain filesystem by setting the property to false: Because we don't want to backup everything, we can exclude certain filesystem by setting the property to false:
```console ```console
@ -163,6 +165,13 @@ rpool/swap autobackup:offsite1 false
... ...
``` ```
The autobackup-property can have 3 values:
* ```true```: Backup the dataset and all its children
* ```false```: Dont backup the dataset and all its children. (used to exclude certain datasets)
* ```child```: Only backup the children off the dataset, not the dataset itself.
Only use the zfs-command to set these properties, not the zpool command.
### Running zfs-autobackup ### Running zfs-autobackup
Run the script on the backup server and pull the data from the server specified by --ssh-source. Run the script on the backup server and pull the data from the server specified by --ssh-source.
@ -654,7 +663,7 @@ for HOST in $HOSTS; do
done done
``` ```
This script will also send the backup status to Zabbix. (if you've installed my zabbix-job-status script) This script will also send the backup status to Zabbix. (if you've installed my zabbix-job-status script https://github.com/psy0rz/stuff/tree/master/zabbix-jobs)
# Sponsor list # Sponsor list

View File

@ -1,7 +1,7 @@
from basetest import * from basetest import *
class TestZfsNode(unittest2.TestCase): class TestExternalFailures(unittest2.TestCase):
def setUp(self): def setUp(self):
prepare_zpools() prepare_zpools()
@ -259,8 +259,28 @@ test_target1/test_source2/fs2/sub@test-20101111000002
with patch('time.strftime', return_value="20101111000001"): with patch('time.strftime', return_value="20101111000001"):
self.assertTrue(ZfsAutobackup("test test_target1 --verbose --allow-empty".split(" ")).run()) self.assertTrue(ZfsAutobackup("test test_target1 --verbose --allow-empty".split(" ")).run())
############# TODO: #UPDATE: offcourse the one thing that wasn't tested had a bug :( (in ExecuteNode.run()).
def test_ignoretransfererrors(self): def test_ignoretransfererrors(self):
self.skipTest( self.skipTest("Not sure how to implement a test for this without some serious hacking and patching.")
"todo: create some kind of situation where zfs recv exits with an error but transfer is still ok (happens in practice with acltype)")
# #recreate target pool without any features
# # shelltest("zfs set compress=on test_source1; zpool destroy test_target1; zpool create test_target1 -o feature@project_quota=disabled /dev/ram2")
#
# with patch('time.strftime', return_value="20101111000000"):
# self.assertFalse(ZfsAutobackup("test test_target1 --verbose --allow-empty --no-progress".split(" ")).run())
#
# r = shelltest("zfs list -H -o name -r -t all test_target1")
#
# self.assertMultiLineEqual(r, """
# test_target1
# test_target1/test_source1
# test_target1/test_source1/fs1
# test_target1/test_source1/fs1@test-20101111000002
# test_target1/test_source1/fs1/sub
# test_target1/test_source1/fs1/sub@test-20101111000002
# test_target1/test_source2
# test_target1/test_source2/fs2
# test_target1/test_source2/fs2/sub
# test_target1/test_source2/fs2/sub@test-20101111000002
# """)

View File

@ -4,6 +4,7 @@ import subprocess
from zfs_autobackup.LogStub import LogStub from zfs_autobackup.LogStub import LogStub
class ExecuteNode(LogStub): class ExecuteNode(LogStub):
"""an endpoint to execute local or remote commands via ssh""" """an endpoint to execute local or remote commands via ssh"""
@ -46,17 +47,23 @@ class ExecuteNode(LogStub):
def run(self, cmd, inp=None, tab_split=False, valid_exitcodes=None, readonly=False, hide_errors=False, pipe=False, def run(self, cmd, inp=None, tab_split=False, valid_exitcodes=None, readonly=False, hide_errors=False, pipe=False,
return_stderr=False): return_stderr=False):
"""run a command on the node cmd: the actual command, should be a list, where the first item is the command """run a command on the node.
and the rest are parameters. input: Can be None, a string or a pipe-handle you got from another run()
tab_split: split tabbed files in output into a list valid_exitcodes: list of valid exit codes for this :param cmd: the actual command, should be a list, where the first item is the command
command (checks exit code of both sides of a pipe) readonly: make this True if the command doesn't make any and the rest are parameters.
changes and is safe to execute in testmode hide_errors: don't show stderr output as error, instead show it as :param inp: Can be None, a string or a pipe-handle you got from another run()
debugging output (use to hide expected errors) pipe: Instead of executing, return a pipe-handle to be used to :param tab_split: split tabbed files in output into a list
input to another run() command. (just like a | in linux) return_stderr: return both stdout and stderr as a :param valid_exitcodes: list of valid exit codes for this command (checks exit code of both sides of a pipe)
tuple. (only returns stderr from this side of the pipe) Use [] to accept all exit codes.
:param readonly: make this True if the command doesn't make any changes and is safe to execute in testmode
:param hide_errors: don't show stderr output as error, instead show it as debugging output (use to hide expected errors)
:param pipe: Instead of executing, return a pipe-handle to be used to
input to another run() command. (just like a | in linux)
:param return_stderr: return both stdout and stderr as a tuple. (normally only returns stdout)
""" """
if not valid_exitcodes: if valid_exitcodes is None:
valid_exitcodes = [0] valid_exitcodes = [0]
encoded_cmd = [] encoded_cmd = []
@ -196,4 +203,4 @@ class ExecuteNode(LogStub):
if return_stderr: if return_stderr:
return output_lines, error_lines return output_lines, error_lines
else: else:
return output_lines return output_lines

View File

@ -12,7 +12,7 @@ from zfs_autobackup.ThinnerRule import ThinnerRule
class ZfsAutobackup: class ZfsAutobackup:
"""main class""" """main class"""
VERSION = "3.0.1-beta7" VERSION = "3.0.1-beta8"
HEADER = "zfs-autobackup v{} - Copyright 2020 E.H.Eefting (edwin@datux.nl)".format(VERSION) HEADER = "zfs-autobackup v{} - Copyright 2020 E.H.Eefting (edwin@datux.nl)".format(VERSION)
def __init__(self, argv, print_arguments=True): def __init__(self, argv, print_arguments=True):
@ -23,8 +23,7 @@ class ZfsAutobackup:
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description=self.HEADER, description=self.HEADER,
epilog='When a filesystem fails, zfs_backup will continue and report the number of failures at that end. ' epilog='Full manual at: https://github.com/psy0rz/zfs_autobackup')
'Also the exit code will indicate the number of failures. Full manual at: https://github.com/psy0rz/zfs_autobackup')
parser.add_argument('--ssh-config', default=None, help='Custom ssh client config') parser.add_argument('--ssh-config', default=None, help='Custom ssh client config')
parser.add_argument('--ssh-source', default=None, parser.add_argument('--ssh-source', default=None,
help='Source host to get backup from. (user@hostname) Default %(default)s.') help='Source host to get backup from. (user@hostname) Default %(default)s.')

View File

@ -489,8 +489,8 @@ class ZfsDataset:
if self.zfs_node.readonly: if self.zfs_node.readonly:
self.force_exists = True self.force_exists = True
# check if transfer was really ok (exit codes have been wrong before due to bugs in zfs-utils and can be # check if transfer was really ok (exit codes have been wrong before due to bugs in zfs-utils and some
# ignored by some parameters) # errors should be ignored, thats where the ignore_exitcodes is for.)
if not self.exists: if not self.exists:
self.error("error during transfer") self.error("error during transfer")
raise (Exception("Target doesn't exist after transfer, something went wrong.")) raise (Exception("Target doesn't exist after transfer, something went wrong."))