bundles/backup-{client,server}: introduce
This commit is contained in:
parent
59c1cb8551
commit
f71653e3ce
23 changed files with 171 additions and 0 deletions
31
bundles/backup-client/files/generate-backup
Normal file
31
bundles/backup-client/files/generate-backup
Normal file
|
@ -0,0 +1,31 @@
|
|||
#!/bin/bash
|
||||
|
||||
statusfile=/var/tmp/backup.monitoring
|
||||
ssh_login="${username}@${server}"
|
||||
|
||||
if ! [[ -f /etc/backup.priv ]]
|
||||
then
|
||||
echo "abort_no_key" > "$statusfile"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rsync_errors=""
|
||||
% for path in sorted(paths):
|
||||
rsync -zaAP --numeric-ids --delete --relative \
|
||||
--rsync-path="/usr/bin/rsync --fake-super" \
|
||||
-e "ssh -o IdentityFile=/etc/backup.priv -o StrictHostKeyChecking=accept-new" \
|
||||
"${path}" "$ssh_login":backups/
|
||||
|
||||
exitcode=$?
|
||||
if (( exitcode != 0 )) && (( exitcode != 24 ))
|
||||
then
|
||||
rsync_errors+=" $ret"
|
||||
fi
|
||||
% endfor
|
||||
|
||||
if [[ -n "$rsync_errors" ]]
|
||||
then
|
||||
echo "rsync_error$rsync_errors" > "$statusfile"
|
||||
fi
|
||||
|
||||
echo "ok" > "$statusfile"
|
25
bundles/backup-client/items.py
Normal file
25
bundles/backup-client/items.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
from os.path import join
|
||||
|
||||
if node.metadata['backups'].get('exclude_from_backups', False):
|
||||
files = {
|
||||
'/etc/backup.priv': {
|
||||
'delete': True,
|
||||
},
|
||||
}
|
||||
else:
|
||||
files = {
|
||||
'/usr/local/bin/generate-backup': {
|
||||
'content_type': 'mako',
|
||||
'context': {
|
||||
'username': node.metadata['backup-client']['user-name'],
|
||||
'server': node.metadata['backup-client']['server'],
|
||||
'paths': node.metadata.get('backups', {}).get('paths', {}),
|
||||
},
|
||||
'mode': '0700',
|
||||
},
|
||||
'/etc/backup.priv': {
|
||||
'content': repo.vault.decrypt_file(join('backup', 'keys', f'{node.name}.key.vault')),
|
||||
'mode': '0400',
|
||||
},
|
||||
}
|
||||
|
21
bundles/backup-client/metadata.py
Normal file
21
bundles/backup-client/metadata.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
from hashlib import md5
|
||||
|
||||
defaults = {
|
||||
'backup-client': {
|
||||
# unix user names cannot be longer than 32 characters.
|
||||
# bundlewrap raises an error if the name is longer than 30 chars.
|
||||
'user-name': 'c-' + md5(node.name.encode('UTF-8')).hexdigest()[:28],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@metadata_reactor
|
||||
def cron(metadata):
|
||||
if metadata.get('backups/exclude_from_backups', False):
|
||||
return {}
|
||||
|
||||
return {
|
||||
'cron': {
|
||||
'backup': '{} 1 * * * root /usr/local/bin/generate-backup',
|
||||
},
|
||||
}
|
28
bundles/backup-server/items.py
Normal file
28
bundles/backup-server/items.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
assert node.has_bundle('zfs')
|
||||
|
||||
from os.path import join
|
||||
|
||||
for nodename, config in node.metadata.get('backup-server', {}).get('clients', {}).items():
|
||||
with open(join(repo.path, 'data', 'backup', 'keys', f'{nodename}.pub'), 'r') as f:
|
||||
pubkey = f.read().strip()
|
||||
|
||||
users[config['user']] = {
|
||||
'home': f'/srv/backups/{nodename}',
|
||||
}
|
||||
|
||||
files[f'/srv/backups/{nodename}/.ssh/authorized_keys'] = {
|
||||
'content': pubkey,
|
||||
'owner': config['user'],
|
||||
'mode': '0400',
|
||||
'needs': {
|
||||
'bundle:zfs',
|
||||
},
|
||||
}
|
||||
|
||||
directories[f'/srv/backups/{nodename}/backups'] = {
|
||||
'owner': config['user'],
|
||||
'mode': '0700',
|
||||
'needs': {
|
||||
'bundle:zfs',
|
||||
},
|
||||
}
|
45
bundles/backup-server/metadata.py
Normal file
45
bundles/backup-server/metadata.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
@metadata_reactor
|
||||
def get_my_clients(metadata):
|
||||
my_clients = {}
|
||||
|
||||
for rnode in repo.nodes:
|
||||
if rnode.metadata.get('backups/exclude_from_backups', False):
|
||||
continue
|
||||
|
||||
my_clients[rnode.name] = {
|
||||
'user': rnode.metadata.get('backup-client/user-name'),
|
||||
}
|
||||
|
||||
return {
|
||||
'backup-server': {
|
||||
'clients': my_clients,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@metadata_reactor
|
||||
def zfs(metadata):
|
||||
zfs_datasets = {}
|
||||
zfs_retains = {}
|
||||
retain_defaults = {
|
||||
'weekly': 4,
|
||||
'monthly': 6,
|
||||
}
|
||||
|
||||
for client in metadata.get('backup-server/clients', {}).keys():
|
||||
dataset = '{}/{}'.format(metadata.get('backup-server/zfs-base'), client)
|
||||
|
||||
zfs_datasets[dataset] = {
|
||||
'mountpoint': '/srv/backups/{}'.format(client),
|
||||
}
|
||||
|
||||
zfs_retains[dataset] = retain_defaults.copy()
|
||||
|
||||
return {
|
||||
'zfs': {
|
||||
'datasets': zfs_datasets,
|
||||
'snapshots': {
|
||||
'retain_per_dataset': zfs_retains,
|
||||
},
|
||||
},
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue