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-*',
|
|
|
|
},
|
|
|
|
},
|
2022-04-06 16:13:23 +00:00
|
|
|
'zfs': {
|
|
|
|
# The whole point of doing backups is to keep them for a long
|
|
|
|
# time, which eliminates the need for this check.
|
|
|
|
'enable_old_snapshots_check': False,
|
|
|
|
},
|
2020-11-14 13:35:54 +00:00
|
|
|
}
|
|
|
|
|
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 = {}
|
2022-01-05 08:53:18 +00:00
|
|
|
retain_defaults = {
|
|
|
|
'daily': 14,
|
|
|
|
'weekly': 4,
|
|
|
|
'monthly': 6,
|
|
|
|
}
|
2020-11-13 11:36:52 +00:00
|
|
|
|
|
|
|
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'),
|
2022-02-12 18:04:15 +00:00
|
|
|
'one_backup_every_hours': rnode.metadata.get('backup-client/one_backup_every_hours', 24),
|
2022-01-05 08:53:18 +00:00
|
|
|
'retain': {
|
|
|
|
'daily': rnode.metadata.get('backups/retain/daily', retain_defaults['daily']),
|
|
|
|
'weekly': rnode.metadata.get('backups/retain/weekly', retain_defaults['weekly']),
|
|
|
|
'monthly': rnode.metadata.get('backups/retain/monthly', retain_defaults['monthly']),
|
|
|
|
},
|
2020-11-13 11:36:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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',
|
2022-01-05 08:53:18 +00:00
|
|
|
'zfs/snapshots/snapshot_never',
|
2021-01-07 17:44:38 +00:00
|
|
|
)
|
2022-01-04 14:08:52 +00:00
|
|
|
def zfs_datasets_and_snapshots(metadata):
|
2020-11-13 11:36:52 +00:00
|
|
|
zfs_datasets = {}
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
'zfs': {
|
|
|
|
'datasets': zfs_datasets,
|
|
|
|
'snapshots': {
|
2022-01-05 08:53:18 +00:00
|
|
|
'snapshot_never': {
|
|
|
|
metadata.get('backup-server/zfs-base'),
|
|
|
|
},
|
2020-11-13 11:36:52 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2022-01-05 21:44:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
@metadata_reactor.provides(
|
|
|
|
'icinga2_api/backup-server/services',
|
|
|
|
)
|
|
|
|
def monitoring(metadata):
|
|
|
|
services = {}
|
|
|
|
|
2022-01-05 21:56:23 +00:00
|
|
|
for client, config in metadata.get('backup-server/clients', {}).items():
|
|
|
|
if config.get('exclude_from_monitoring', False):
|
|
|
|
continue
|
|
|
|
|
2022-01-05 21:44:55 +00:00
|
|
|
services[f'BACKUPS FOR NODE {client}'] = {
|
2022-02-12 18:04:15 +00:00
|
|
|
'command_on_monitored_host': 'sudo /usr/local/share/icinga/plugins/check_backup_for_node {} {}'.format(
|
|
|
|
client,
|
|
|
|
config['one_backup_every_hours'],
|
|
|
|
),
|
2022-03-13 07:30:59 +00:00
|
|
|
'vars.sshmon_timeout': 20,
|
2022-01-05 21:44:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
'icinga2_api': {
|
|
|
|
'backup-server': {
|
|
|
|
'services': services,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|