diff --git a/actions/snapshot b/actions/snapshot index 146311289..482a55181 100755 --- a/actions/snapshot +++ b/actions/snapshot @@ -76,10 +76,12 @@ def subcommand_setup(_): def _set_default_config(): # Software snapshots' default minimum age: 30 days - command = ['snapper','set-config', 'TIMELINE_CREATE=yes', - 'TIMELINE_LIMIT_HOURLY=10', 'TIMELINE_LIMIT_MONTHLY=2', - 'TIMELINE_LIMIT_WEEKLY=2', 'TIMELINE_LIMIT_YEARLY=0', - 'TIMELINE_LIMIT_DAILY=3', 'NUMBER_MIN_AGE=2592000'] + command = [ + 'snapper', 'set-config', 'TIMELINE_CREATE=yes', + 'TIMELINE_LIMIT_HOURLY=10', 'TIMELINE_LIMIT_MONTHLY=2', + 'TIMELINE_LIMIT_WEEKLY=2', 'TIMELINE_LIMIT_YEARLY=0', + 'TIMELINE_LIMIT_DAILY=3', 'NUMBER_MIN_AGE=1296000' + ] subprocess.run(command, check=True) diff --git a/plinth/modules/snapshot/__init__.py b/plinth/modules/snapshot/__init__.py index 1a7328a66..e0204ba9c 100644 --- a/plinth/modules/snapshot/__init__.py +++ b/plinth/modules/snapshot/__init__.py @@ -35,11 +35,10 @@ description = [ _('Snapshots allows creating and managing filesystem snapshots. These can ' 'be used to roll back the system to a previously known good state in ' 'case of unwanted changes to the system.'), - _('Automatic snapshots are taken every hour, day, month and year. Older ' - 'snapshots are automatically deleted keeping 10 of each kind and 50 in ' - 'total. Although snapshots are efficient and only store the ' + _('Automatic snapshots are taken every hour, day, month and year. ' + 'Although snapshots are efficient and only store the ' 'differences, they may be deleted to reclaim free space. Individual ' - 'files from older snapshots can be accessed by visiting ".snapshots" ' + 'files from older snapshots can be accessed by visiting "/.snapshots" ' 'directory in the filesystem. Snapshots are not a replacement for ' 'backups.') ] @@ -62,11 +61,12 @@ def setup(helper, old_version=None): def get_configuration(): output = actions.superuser_run('snapshot', ['get-config']) output = json.loads(output) - return {'enable_timeline_snapshots': output['TIMELINE_CREATE'] == 'yes', - 'hourly_limit': output['TIMELINE_LIMIT_HOURLY'], - 'daily_limit': output['TIMELINE_LIMIT_DAILY'], - 'weekly_limit': output['TIMELINE_LIMIT_WEEKLY'], - 'yearly_limit': output['TIMELINE_LIMIT_YEARLY'], - 'monthly_limit': output['TIMELINE_LIMIT_MONTHLY'], - 'number_min_age': round(int(output['NUMBER_MIN_AGE']) / 86400), - 'timeline_min_age': round(int(output['TIMELINE_MIN_AGE']) / 86400)} + return { + 'enable_timeline_snapshots': output['TIMELINE_CREATE'] == 'yes', + 'hourly_limit': output['TIMELINE_LIMIT_HOURLY'], + 'daily_limit': output['TIMELINE_LIMIT_DAILY'], + 'weekly_limit': output['TIMELINE_LIMIT_WEEKLY'], + 'yearly_limit': output['TIMELINE_LIMIT_YEARLY'], + 'monthly_limit': output['TIMELINE_LIMIT_MONTHLY'], + 'number_min_age': round(int(output['NUMBER_MIN_AGE']) / 86400), + } diff --git a/plinth/modules/snapshot/forms.py b/plinth/modules/snapshot/forms.py index 366592de1..d36c226a9 100644 --- a/plinth/modules/snapshot/forms.py +++ b/plinth/modules/snapshot/forms.py @@ -27,9 +27,30 @@ class SnapshotForm(forms.Form): label=_('Enable Timeline Snapshots'), required=False, help_text=_( 'Uncheck this to disable timeline snapshots ' '(hourly, daily, monthly and yearly).')) - hourly_limit = forms.IntegerField(label=_('Hourly Snapshots Limit'),required=False, min_value=0) - daily_limit = forms.IntegerField(label=_('Daily Snapshots Limit'),required=False, min_value=0) - weekly_limit = forms.IntegerField(label=_('Weekly Snapshots Limit'),required=False, min_value=0) - monthly_limit = forms.IntegerField(label=_('Monthly Snapshots Limit'),required=False, min_value=0) - yearly_limit = forms.IntegerField(label=_('Yearly Snapshots Limit'),required=False, min_value=0) - number_min_age = forms.IntegerField(label=_('Software Snapshots Minimum Age (days)'), required=False, min_value=0) + + hourly_limit = forms.IntegerField( + label=_('Hourly Snapshots Limit'), min_value=0, + help_text=('Snapper will only keep this number of hourly snapshots.')) + + daily_limit = forms.IntegerField( + label=_('Daily Snapshots Limit'), min_value=0, + help_text=('Snapper will only keep this number of daily snapshots.')) + + weekly_limit = forms.IntegerField( + label=_('Weekly Snapshots Limit'), min_value=0, + help_text=('Snapper will only keep this number of weekly snapshots.')) + + monthly_limit = forms.IntegerField( + label=_('Monthly Snapshots Limit'), min_value=0, + help_text=('Snapper will only keep this number of monthly snapshots.')) + + yearly_limit = forms.IntegerField( + label=_('Yearly Snapshots Limit'), min_value=0, + help_text=('Snapper will only keep this number of yearly snapshots. ' + 'The default is 0 (disabled).')) + + number_min_age = forms.IntegerField( + label=_('Delete Software Snapshots older than (days)'), min_value=0, + help_text=_( + 'Software snapshots older than this will be deleted. ' + 'This does not limit the number of software snapshots created.')) diff --git a/plinth/modules/snapshot/views.py b/plinth/modules/snapshot/views.py index bb5ab416e..439fbc339 100644 --- a/plinth/modules/snapshot/views.py +++ b/plinth/modules/snapshot/views.py @@ -53,44 +53,47 @@ def index(request): output = actions.superuser_run('snapshot', ['list']) snapshots = json.loads(output) has_deletable_snapshots = any( - [snapshot for snapshot in snapshots[1:] - if not snapshot['is_default']]) + [snapshot for snapshot in snapshots[1:] if not snapshot['is_default']]) - return TemplateResponse(request, 'snapshot.html', { - 'title': snapshot_module.name, - 'description': snapshot_module.description, - 'snapshots': snapshots, - 'has_deletable_snapshots': has_deletable_snapshots, - 'form': form - }) - - -def update_key_configuration(key, old_status, new_status, stamp,threshold): - if old_status[key] != new_status[key]: - actions.superuser_run( - 'snapshot', - ['configure', stamp.format(threshold)]) + return TemplateResponse( + request, 'snapshot.html', { + 'title': snapshot_module.name, + 'description': snapshot_module.description, + 'snapshots': snapshots, + 'has_deletable_snapshots': has_deletable_snapshots, + 'form': form + }) def update_configuration(request, old_status, new_status): """Update configuration of snapshots.""" - try: - update_key_configuration('enable_timeline_snapshots', old_status, new_status, 'TIMELINE_CREATE={}', yes_or_no(new_status['enable_timeline_snapshots'])) - update_key_configuration('hourly_limit',old_status,new_status,'TIMELINE_LIMIT_HOURLY={}', new_status['hourly_limit']) - update_key_configuration('daily_limit', old_status, new_status, 'TIMELINE_LIMIT_DAILY={}', new_status['daily_limit']) - update_key_configuration('weekly_limit', old_status, new_status, 'TIMELINE_LIMIT_WEEKLY={}', new_status['weekly_limit']) - update_key_configuration('monthly_limit', old_status, new_status, 'TIMELINE_LIMIT_MONTHLY={}', new_status['monthly_limit']) - update_key_configuration('yearly_limit', old_status, new_status, 'TIMELINE_LIMIT_YEARLY={}', new_status['yearly_limit']) - update_key_configuration('number_min_age', old_status, new_status, 'NUMBER_MIN_AGE={}', int(new_status['number_min_age'])*86400) - actions.superuser_run( - 'snapshot', - ['configure', 'NUMBER_LIMIT=0-0']) - actions.superuser_run( - 'snapshot', - ['configure', 'NUMBER_LIMIT_IMPORTANT=4-10']) - messages.success(request, - _('Timeline Snapshots configuration updated')) + def update_key_configuration(key, stamp, threshold): + if old_status[key] != new_status[key]: + actions.superuser_run( + 'snapshot', ['configure', stamp.format(threshold)]) + + try: + update_key_configuration( + 'enable_timeline_snapshots', 'TIMELINE_CREATE={}', + yes_or_no(new_status['enable_timeline_snapshots'])) + update_key_configuration('hourly_limit', 'TIMELINE_LIMIT_HOURLY={}', + new_status['hourly_limit']) + update_key_configuration('daily_limit', 'TIMELINE_LIMIT_DAILY={}', + new_status['daily_limit']) + update_key_configuration('weekly_limit', 'TIMELINE_LIMIT_WEEKLY={}', + new_status['weekly_limit']) + update_key_configuration('monthly_limit', 'TIMELINE_LIMIT_MONTHLY={}', + new_status['monthly_limit']) + update_key_configuration('yearly_limit', 'TIMELINE_LIMIT_YEARLY={}', + new_status['yearly_limit']) + update_key_configuration('number_min_age', 'NUMBER_MIN_AGE={}', + int(new_status['number_min_age']) * 86400) + + actions.superuser_run('snapshot', ['configure', 'NUMBER_LIMIT=0']) + actions.superuser_run('snapshot', + ['configure', 'NUMBER_LIMIT_IMPORTANT=4-10']) + messages.success(request, _('Storage snapshots configuration updated')) except ActionError as exception: messages.error(request, _('Action error: {0} [{1}] [{2}]').format( @@ -103,7 +106,8 @@ def delete(request, number): if request.method == 'POST': actions.superuser_run('snapshot', ['delete', number]) messages.success( - request, _('Deleted snapshot #{number}.').format(number=number)) + request, + _('Deleted snapshot #{number}.').format(number=number)) return redirect(reverse('snapshot:index')) output = actions.superuser_run('snapshot', ['list'])