2020-11-14 13:35:54 +00:00
|
|
|
defaults = {
|
2022-01-04 16:14:55 +00:00
|
|
|
'backup-server': {
|
|
|
|
'my_ssh_port': 22,
|
|
|
|
},
|
2020-11-14 13:35:54 +00:00
|
|
|
'openssh': {
|
|
|
|
'allowed_users': {
|
|
|
|
# Usernames for backup clients always start with 'c-'
|
|
|
|
'c-*',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2021-01-07 17:44:38 +00:00
|
|
|
@metadata_reactor.provides(
|
|
|
|
'backup-server/clients',
|
2022-01-04 16:14:55 +00:00
|
|
|
'backup-server/my_hostname',
|
2021-01-07 17:44:38 +00:00
|
|
|
)
|
2020-11-13 11:36:52 +00:00
|
|
|
def get_my_clients(metadata):
|
|
|
|
my_clients = {}
|
|
|
|
|
|
|
|
for rnode in repo.nodes:
|
2021-01-16 21:22:51 +00:00
|
|
|
if not rnode.has_bundle('backup-client') or rnode.metadata.get('backups/exclude_from_backups', False):
|
2020-11-13 11:36:52 +00:00
|
|
|
continue
|
|
|
|
|
2022-01-04 16:14:55 +00:00
|
|
|
if node.name != rnode.metadata.get('backup-client/target'):
|
|
|
|
continue
|
|
|
|
|
2020-11-13 11:36:52 +00:00
|
|
|
my_clients[rnode.name] = {
|
|
|
|
'user': rnode.metadata.get('backup-client/user-name'),
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
'backup-server': {
|
|
|
|
'clients': my_clients,
|
2022-01-04 16:14:55 +00:00
|
|
|
'my_hostname': metadata.get('hostname'),
|
2020-11-13 11:36:52 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-01-04 14:08:52 +00:00
|
|
|
@metadata_reactor.provides(
|
|
|
|
'backup-server/zfs-base',
|
|
|
|
'dm-crypt/encrypted-devices',
|
|
|
|
'zfs/pools',
|
|
|
|
)
|
|
|
|
def zfs_pool(metadata):
|
|
|
|
if not metadata.get('backup-server/encrypted-devices', {}):
|
|
|
|
return {}
|
|
|
|
|
|
|
|
crypt_devices = {}
|
|
|
|
pool_devices = set()
|
|
|
|
unlock_actions = set()
|
|
|
|
|
|
|
|
for number, (device, passphrase) in enumerate(sorted(metadata.get('backup-server/encrypted-devices', {}).items())):
|
|
|
|
crypt_devices[device] = {
|
|
|
|
'dm-name': f'backup{number}',
|
|
|
|
'passphrase': passphrase,
|
|
|
|
}
|
|
|
|
pool_devices.add(f'/dev/mapper/backup{number}')
|
|
|
|
unlock_actions.add(f'action:dm-crypt_open_backup{number}')
|
|
|
|
|
|
|
|
pool_opts = {
|
|
|
|
'devices': pool_devices,
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(pool_devices) > 2:
|
|
|
|
pool_opts['type'] = 'raidz'
|
|
|
|
elif len(pool_devices) > 1:
|
|
|
|
pool_opts['type'] = 'mirror'
|
|
|
|
|
|
|
|
return {
|
|
|
|
'backup-server': {
|
|
|
|
'zfs-base': 'backups',
|
|
|
|
},
|
|
|
|
'dm-crypt': {
|
|
|
|
'encrypted-devices': crypt_devices,
|
|
|
|
},
|
|
|
|
'zfs': {
|
|
|
|
'pools': {
|
|
|
|
'backups': {
|
|
|
|
'when_creating': {
|
|
|
|
'config': [
|
|
|
|
pool_opts,
|
|
|
|
],
|
|
|
|
},
|
|
|
|
'needs': unlock_actions,
|
2022-01-04 14:24:22 +00:00
|
|
|
# That's a bit hacky. We do it this way to auto-import
|
|
|
|
# the pool after decrypting the devices. Otherwise
|
|
|
|
# the pool wouldn't exist, which leads to bundlewrap
|
|
|
|
# trying to re-create the pool.
|
|
|
|
# Also, -N to not auto-mount anything.
|
|
|
|
'unless': 'zpool import -N backups',
|
2022-01-04 14:08:52 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-01-07 17:44:38 +00:00
|
|
|
@metadata_reactor.provides(
|
|
|
|
'zfs/datasets',
|
|
|
|
'zfs/snapshots/retain_per_dataset',
|
|
|
|
)
|
2022-01-04 14:08:52 +00:00
|
|
|
def zfs_datasets_and_snapshots(metadata):
|
2020-11-13 11:36:52 +00:00
|
|
|
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),
|
2021-06-25 18:04:10 +00:00
|
|
|
'compression': 'on',
|
2020-11-13 11:36:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
zfs_retains[dataset] = retain_defaults.copy()
|
|
|
|
|
|
|
|
return {
|
|
|
|
'zfs': {
|
|
|
|
'datasets': zfs_datasets,
|
|
|
|
'snapshots': {
|
|
|
|
'retain_per_dataset': zfs_retains,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|