-
Notifications
You must be signed in to change notification settings - Fork 49
/
purge.py
177 lines (150 loc) · 5.8 KB
/
purge.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
"""
Command to delete old backups
"""
import itertools
import logging
from holland.core.command import Command
from holland.core.config import HOLLANDCFG, ConfigError
from holland.core.spool import CONFIGSPEC, SPOOL
from holland.core.util.fmt import format_bytes
LOG = logging.getLogger(__name__)
class Purge(Command):
"""${cmd_usage}
Purge the requested job runs
${cmd_option_list}
"""
name = "purge"
aliases = ["pg"]
args = [["--dry-run", "-n"], ["--all", "-a"], ["--force", "-f"], ["--execute"]]
kargs = [
{
"action": "store_true",
"dest": "force",
"default": False,
"help": "Print what would be purged without actually purging",
},
{
"action": "store_true",
"default": False,
"help": "When purging a backupset purge everything rather than \
using the retention count from the active configuration",
},
{
"action": "store_true",
"default": False,
"help": "Execute the purge (disable dry-run). Alias for --execute",
},
{
"action": "store_true",
"dest": "force",
"help": "Execute the purge (disable dry-run)",
},
]
description = "Purge the requested job runs"
def run(self, cmd, opts, *backups):
error = 0
if not backups:
LOG.info(
"No backupsets specified - using backupsets from %s",
HOLLANDCFG.filename,
)
backups = HOLLANDCFG.lookup("holland.backupsets")
if not backups:
LOG.warning("Nothing to purge")
return 0
if not opts.force:
LOG.warning("Running in dry-run mode. Use --execute to do a real purge.")
for name in backups:
if "/" not in name:
backupset = SPOOL.find_backupset(name)
if not backupset:
LOG.error("Failed to find backupset '%s'", name)
error = 1
continue
purge_backupset(backupset, opts.force, opts.all)
else:
backup = SPOOL.find_backup(name)
if not backup:
LOG.error("Failed to find single backup '%s'", name)
error = 1
continue
purge_backup(backup, opts.force)
if opts.force:
SPOOL.find_backupset(backup.backupset).update_symlinks()
return error
def purge_backupset(backupset, force=False, all_backups=False):
"""Purge a whole backupset either entirely or per the configured
retention count
:param backupset: Backupset object to purge
:param force: Force the purge - this is not a dry-run
:param all_backupsets: purge all backups regardless of configured
retention count
"""
if all_backups:
retention_count = 0
else:
try:
config = HOLLANDCFG.backupset(backupset.name)
config.validate_config(CONFIGSPEC, suppress_warnings=True)
except (IOError, ConfigError) as exc:
LOG.error("Failed to load backupset '%s': %s", backupset.name, exc)
LOG.error("Aborting, because I could not tell how many backups to preserve.")
LOG.error(
"You can still purge the backupset by using the --all "
"option or specifying specific backups to purge"
)
else:
retention_count = config["holland:backup"]["backups-to-keep"]
LOG.info("Evaluating purge for backupset %s", backupset.name)
LOG.info(
"Retaining up to %d backup%s",
retention_count,
"s"[0 : bool(retention_count)],
)
backups = []
size = 0
backup_list = backupset.list_backups(reverse=True)
for backup in itertools.islice(backup_list, retention_count, None):
backups.append(backup)
config = backup.config["holland:backup"]
size += int(config["on-disk-size"])
LOG.info(" %d total backups", len(backup_list))
for backup in backup_list:
LOG.info(" * %s", backup.path)
LOG.info(" %d backups to keep", len(backup_list) - len(backups))
for backup in backup_list[0 : -len(backups)]:
LOG.info(" + %s", backup.path)
LOG.info(" %d backups to purge", len(backups))
for backup in backups:
LOG.info(" - %s", backup.path)
LOG.info(" %s total to purge", format_bytes(size))
if force:
count = 0
for backup in backupset.purge(retention_count):
count += 1
LOG.info("Purged %s", backup.name)
if count == 0:
LOG.info("No backups purged.")
else:
LOG.info("Purged %d backup%s", count, "s"[0 : bool(count)])
else:
LOG.info("Skipping purge in dry-run mode.")
config = backup.config["holland:backup"]
backupset.update_symlinks(
enable=config["create-symlinks"], relative=config["relative-symlinks"]
)
def purge_backup(backup, force=False):
"""Purge a single backup
:param backup: Backup object to purge
:param force: Force the purge - this is not a dry-run
"""
if not force:
config = backup.config["holland:backup"]
LOG.info(
"Would purge single backup '%s' %s",
backup.name,
format_bytes(int(config["on-disk-size"])),
)
else:
backup.purge()
LOG.info("Purged %s", backup.name)