Giter Club home page Giter Club logo

python-rotate-backups's Introduction

rotate-backups: Simple command line interface for backup rotation

https://travis-ci.org/xolox/python-rotate-backups.svg?branch=master https://coveralls.io/repos/xolox/python-rotate-backups/badge.svg?branch=master

Backups are good for you. Most people learn this the hard way (including me). Nowadays my Linux laptop automatically creates a full system snapshot every four hours by pushing changed files to an rsync daemon running on the server in my home network and creating a snapshot afterwards using the cp -al command (the article Easy Automated Snapshot-Style Backups with Linux and Rsync explains the basic technique). The server has a second disk attached which asynchronously copies from the main disk so that a single disk failure doesn't wipe all of my backups (the "time delayed replication" aspect has also proven to be very useful).

Okay, cool, now I have backups of everything, up to date and going back in time! But I'm running through disk space like crazy... A proper deduplicating filesystem would be awesome but I'm running crappy consumer grade hardware and e.g. ZFS has not been a good experience in the past. So I'm going to have to delete backups...

Deleting backups is never nice, but an easy and proper rotation scheme can help a lot. I wanted to keep things manageable so I wrote a Python script to do it for me. Over the years I actually wrote several variants. Because I kept copy/pasting these scripts around I decided to bring the main features together in a properly documented Python package and upload it to the Python Package Index.

The rotate-backups package is currently tested on cPython 2.7, 3.5+ and PyPy (2.7). It's tested on Linux and Mac OS X and may work on other unixes but definitely won't work on Windows right now.

Dry run mode
Use it. I'm serious. If you don't and rotate-backups eats more backups than intended you have no right to complain ;-)
Flexible rotation
Rotation with any combination of hourly, daily, weekly, monthly and yearly retention periods.
Fuzzy timestamp matching in filenames

The modification times of the files and/or directories are not relevant. If you speak Python regular expressions, here is how the fuzzy matching works:

# Required components.
(?P<year>\d{4}) \D?
(?P<month>\d{2}) \D?
(?P<day>\d{2}) \D?
(
   # Optional components.
   (?P<hour>\d{2}) \D?
   (?P<minute>\d{2}) \D?
   (?P<second>\d{2})?
)?
All actions are logged
Log messages are saved to the system log (e.g. /var/log/syslog) so you can retrace what happened when something seems to have gone wrong.

The rotate-backups package is available on PyPI which means installation should be as simple as:

$ pip install rotate-backups

There's actually a multitude of ways to install Python packages (e.g. the per user site-packages directory, virtual environments or just installing system wide) and I have no intention of getting into that discussion here, so if this intimidates you then read up on your options before returning to these instructions ;-).

There are two ways to use the rotate-backups package: As the command line program rotate-backups and as a Python API. For details about the Python API please refer to the API documentation available on Read the Docs. The command line interface is described below.

Usage: rotate-backups [OPTIONS] [DIRECTORY, ..]

Easy rotation of backups based on the Python package by the same name.

To use this program you specify a rotation scheme via (a combination of) the --hourly, --daily, --weekly, --monthly and/or --yearly options and the directory (or directories) containing backups to rotate as one or more positional arguments.

You can rotate backups on a remote system over SSH by prefixing a DIRECTORY with an SSH alias and separating the two with a colon (similar to how rsync accepts remote locations).

Instead of specifying directories and a rotation scheme on the command line you can also add them to a configuration file. For more details refer to the online documentation (see also the --config option).

Please use the --dry-run option to test the effect of the specified rotation scheme before letting this program loose on your precious backups! If you don't test the results using the dry run mode and this program eats more backups than intended you have no right to complain ;-).

Supported options:

Option Description
-M, --minutely=COUNT In a literal sense this option sets the number of "backups per minute" to preserve during rotation. For most use cases that doesn't make a lot of sense :-) but you can combine the --minutely and --relaxed options to preserve more than one backup per hour. Refer to the usage of the -H, --hourly option for details about COUNT.
-H, --hourly=COUNT

Set the number of hourly backups to preserve during rotation:

  • If COUNT is a number it gives the number of hourly backups to preserve, starting from the most recent hourly backup and counting back in time.
  • Alternatively you can provide an expression that will be evaluated to get a number (e.g. if COUNT is "7 * 2" the result would be 14).
  • You can also pass "always" for COUNT, in this case all hourly backups are preserved.
  • By default no hourly backups are preserved.
-d, --daily=COUNT Set the number of daily backups to preserve during rotation. Refer to the usage of the -H, --hourly option for details about COUNT.
-w, --weekly=COUNT Set the number of weekly backups to preserve during rotation. Refer to the usage of the -H, --hourly option for details about COUNT.
-m, --monthly=COUNT Set the number of monthly backups to preserve during rotation. Refer to the usage of the -H, --hourly option for details about COUNT.
-y, --yearly=COUNT Set the number of yearly backups to preserve during rotation. Refer to the usage of the -H, --hourly option for details about COUNT.
-t, --timestamp-pattern=PATTERN Customize the regular expression pattern that is used to match and extract timestamps from filenames. PATTERN is expected to be a Python compatible regular expression that must define the named capture groups 'year', 'month' and 'day' and may define 'hour', 'minute' and 'second'.
-I, --include=PATTERN Only process backups that match the shell pattern given by PATTERN. This argument can be repeated. Make sure to quote PATTERN so the shell doesn't expand the pattern before it's received by rotate-backups.
-x, --exclude=PATTERN Don't process backups that match the shell pattern given by PATTERN. This argument can be repeated. Make sure to quote PATTERN so the shell doesn't expand the pattern before it's received by rotate-backups.
-j, --parallel

Remove backups in parallel, one backup per mount point at a time. The idea behind this approach is that parallel rotation is most useful when the files to be removed are on different disks and so multiple devices can be utilized at the same time.

Because mount points are per system the -j, --parallel option will also parallelize over backups located on multiple remote systems.

-p, --prefer-recent By default the first (oldest) backup in each time slot is preserved. If you'd prefer to keep the most recent backup in each time slot instead then this option is for you.
-r, --relaxed

By default the time window for each rotation scheme is enforced (this is referred to as strict rotation) but the -r, --relaxed option can be used to alter this behavior. The easiest way to explain the difference between strict and relaxed rotation is using an example:

  • When using strict rotation and the number of hourly backups to preserve is three, only backups created in the relevant time window (the hour of the most recent backup and the two hours leading up to that) will match the hourly frequency.
  • When using relaxed rotation the three most recent backups will all match the hourly frequency (and thus be preserved), regardless of the calculated time window.

If the explanation above is not clear enough, here's a simple way to decide whether you want to customize this behavior or not:

  • If your backups are created at regular intervals and you never miss an interval then strict rotation (the default) is probably the best choice.
  • If your backups are created at irregular intervals then you may want to use the -r, --relaxed option in order to preserve more backups.
-i, --ionice=CLASS Use the "ionice" program to set the I/O scheduling class and priority of the "rm" invocations used to remove backups. CLASS is expected to be one of the values "idle" (3), "best-effort" (2) or "realtime" (1). Refer to the man page of the "ionice" program for details about these values. The numeric values are required by the 'busybox' implementation of 'ionice'.
-c, --config=FILENAME

Load configuration from FILENAME. If this option isn't given the following default locations are searched for configuration files:

  • /etc/rotate-backups.ini and /etc/rotate-backups.d/*.ini
  • ~/.rotate-backups.ini and ~/.rotate-backups.d/*.ini
  • ~/.config/rotate-backups.ini and ~/.config/rotate-backups.d/*.ini

Any available configuration files are loaded in the order given above, so that sections in user-specific configuration files override sections by the same name in system-wide configuration files. For more details refer to the online documentation.

-C, --removal-command=CMD

Change the command used to remove backups. The value of CMD defaults to rm ``-f``R. This choice was made because it works regardless of whether "backups to be rotated" are files or directories or a mixture of both.

As an example of why you might want to change this, CephFS snapshots are represented as regular directory trees that can be deleted at once with a single 'rmdir' command (even though according to POSIX semantics this command should refuse to remove nonempty directories, but I digress).

-u, --use-sudo Enable the use of "sudo" to rotate backups in directories that are not readable and/or writable for the current user (or the user logged in to a remote system over SSH).
-S, --syslog=CHOICE Explicitly enable or disable system logging instead of letting the program figure out what to do. The values '1', 'yes', 'true' and 'on' enable system logging whereas the values '0', 'no', 'false' and 'off' disable it.
-f, --force If a sanity check fails an error is reported and the program aborts. You can use --force to continue with backup rotation instead. Sanity checks are done to ensure that the given DIRECTORY exists, is readable and is writable. If the --removal-command option is given then the last sanity check (that the given location is writable) is skipped (because custom removal commands imply custom semantics).
-n, --dry-run Don't make any changes, just print what would be done. This makes it easy to evaluate the impact of a rotation scheme without losing any backups.
-v, --verbose Increase logging verbosity (can be repeated).
-q, --quiet Decrease logging verbosity (can be repeated).
-h, --help Show this message and exit.

Instead of specifying directories and rotation schemes on the command line you can also add them to a configuration file.

Configuration files are text files in the subset of ini syntax supported by Python's configparser module. They can be located in the following places:

Directory Main configuration file Modular configuration files
/etc /etc/rotate-backups.ini /etc/rotate-backups.d/*.ini
~ ~/.rotate-backups.ini ~/.rotate-backups.d/*.ini
~/.config ~/.config/rotate-backups.ini ~/.config/rotate-backups.d/*.ini

The available configuration files are loaded in the order given above, so that user specific configuration files override system wide configuration files.

You can load a configuration file in a nonstandard location using the command line option --config, in this case the default locations mentioned above are ignored.

Each section in the configuration defines a directory that contains backups to be rotated. The options in each section define the rotation scheme and other options. Here's an example based on how I use rotate-backups to rotate the backups of the Linux installations that I make regular backups of:

# /etc/rotate-backups.ini:
# Configuration file for the rotate-backups program that specifies
# directories containing backups to be rotated according to specific
# rotation schemes.

[/backups/laptop]
hourly = 24
daily = 7
weekly = 4
monthly = 12
yearly = always
ionice = idle

[/backups/server]
daily = 7 * 2
weekly = 4 * 2
monthly = 12 * 4
yearly = always
ionice = idle

[/backups/mopidy]
daily = 7
weekly = 4
monthly = 2
ionice = idle

[/backups/xbmc]
daily = 7
weekly = 4
monthly = 2
ionice = idle

As you can see in the retention periods of the directory /backups/server in the example above you are allowed to use expressions that evaluate to a number (instead of having to write out the literal number).

Here's an example of a configuration for two remote directories:

# SSH as a regular user and use `sudo' to elevate privileges.
[server:/backups/laptop]
use-sudo = yes
hourly = 24
daily = 7
weekly = 4
monthly = 12
yearly = always
ionice = idle

# SSH as the root user (avoids sudo passwords).
[server:/backups/server]
ssh-user = root
hourly = 24
daily = 7
weekly = 4
monthly = 12
yearly = always
ionice = idle

As this example shows you have the option to connect as the root user or to connect as a regular user and use sudo to elevate privileges.

Since publishing rotate-backups I've found that the default rotation algorithm is not to everyone's satisfaction and because the suggested alternatives were just as valid as the choices that I initially made, options were added to expose the alternative behaviors:

Default Alternative
Strict rotation (the time window for each rotation frequency is enforced). Relaxed rotation (time windows are not enforced). Enabled by the -r, --relaxed option.
The oldest backup in each time slot is preserved and newer backups in the time slot are removed. The newest backup in each time slot is preserved and older backups in the time slot are removed. Enabled by the -p, --prefer-recent option.
  • Rotation schemes are defined using the minutely, hourly, daily, weekly, monthly and yearly options, these options support the same values as documented for the command line interface.

  • The timestamp-pattern option can be used to customize the regular expression that's used to extract timestamps from filenames. The value is expected to be a Python compatible regular expression that must contain the named capture groups 'year', 'month' and 'day' and may contain the groups 'hour', 'minute' and 'second'. As an example here is the default regular expression:

    # Required components.
    (?P<year>\d{4} ) \D?
    (?P<month>\d{2}) \D?
    (?P<day>\d{2}  ) \D?
    (?:
        # Optional components.
        (?P<hour>\d{2}  ) \D?
        (?P<minute>\d{2}) \D?
        (?P<second>\d{2})?
    )?
    

    Note how this pattern spans multiple lines: Regular expressions are compiled using the re.VERBOSE flag which means whitespace (including newlines) is ignored.

  • The include-list and exclude-list options define a comma separated list of filename patterns to include or exclude, respectively:

    • Make sure not to quote the patterns in the configuration file, just provide them literally.
    • If an include or exclude list is defined in the configuration file it overrides the include or exclude list given on the command line.
  • The prefer-recent, strict and use-sudo options expect a boolean value (yes, no, true, false, 1 or 0).

  • The removal-command option can be used to customize the command that is used to remove backups.

  • The ionice option expects one of the I/O scheduling class names idle, best-effort or realtime (or the corresponding numbers).

  • The ssh-user option can be used to override the name of the remote SSH account that's used to connect to a remote system.

The basic premise of rotate-backups is fairly simple:

  1. You point rotate-backups at a directory containing timestamped backups.

  2. It will scan the directory for entries (it doesn't matter whether they are files or directories) with a recognizable timestamp in the name.

    Note

    All of the matched directory entries are considered to be backups of the same data source, i.e. there's no filename similarity logic to distinguish unrelated backups that are located in the same directory. If this presents a problem consider using the --include and/or --exclude options.

  3. The user defined rotation scheme is applied to the entries. If this doesn't do what you'd expect it to you can try the --relaxed and/or --prefer-recent options.

  4. The entries to rotate are removed (or printed in dry run).

The latest version of rotate-backups is available on PyPI and GitHub. The documentation is hosted on Read the Docs and includes a changelog. For bug reports please create an issue on GitHub. If you have questions, suggestions, etc. feel free to send me an e-mail at [email protected].

This software is licensed under the MIT license.

© 2020 Peter Odding.

python-rotate-backups's People

Contributors

eyjhb avatar ls-initiatives avatar stathis-alexopoulos avatar tkdbi avatar xolox avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

python-rotate-backups's Issues

[feature] span "hourly" preserver even over days period, etc...

An example, keep 8 hourly backups, keep 4 days backups.

Today, it keep 8 backups created "today" and 1 for each of previous 4 days.

In case, I will create only 4 backups today I might have been interested still to keep additional backups from previous day (ie: like 8 hourly backups would not be affected by today timestamp)

Deletion not expected

Hi !

Version : 4.3

I encountered a behaviour I couldn't understand. This is my config file :

[/home/backup/data/mysql/production/scrap/fr/done]
relaxed = yes
include = '*.xbstream'
weekly = 3
monthly = 2
yearly = always
ionice = idle

And this is the behaviour rotate-backups had when launched :

Sep 27 12:04:02 backup-0001 rotate-backups[19632]: INFO Scanning /home/backup/data/mysql/production/scrap/fr/done for backups ..
Sep 27 12:04:02 backup-0001 rotate-backups[19632]: INFO Found 7 timestamped backups in /home/backup/data/mysql/production/scrap/fr/done.
Sep 27 12:04:02 backup-0001 rotate-backups[19632]: INFO Preserving /home/backup/data/mysql/production/scrap/fr/done/2017-01-13:22-24-57.xbstream (matches 'yearly' retention period) ..
Sep 27 12:04:02 backup-0001 rotate-backups[19632]: INFO Deleting /home/backup/data/mysql/production/scrap/fr/done/2017-05-17-mc_log_range ..
Sep 27 12:04:03 backup-0001 rotate-backups[19632]: INFO Deleting /home/backup/data/mysql/production/scrap/fr/done/2017-05-17-mc_log_range.tar.gz ..
Sep 27 12:04:03 backup-0001 rotate-backups[19632]: INFO Preserving /home/backup/data/mysql/production/scrap/fr/done/2017-08-04:04-45-05.xbstream (matches 'monthly' retention period) ..
Sep 27 12:04:03 backup-0001 rotate-backups[19632]: INFO Preserving /home/backup/data/mysql/production/scrap/fr/done/2017-09-09:19-48-41.xbstream (matches 'weekly' and 'monthly' retention periods) ..
Sep 27 12:04:03 backup-0001 rotate-backups[19632]: INFO Preserving /home/backup/data/mysql/production/scrap/fr/done/2017-09-20:07-17-24.xbstream (matches 'weekly' retention period) ..
Sep 27 12:04:03 backup-0001 rotate-backups[19632]: INFO Preserving /home/backup/data/mysql/production/scrap/fr/done/2017-09-27-mc_log_range (matches 'weekly' retention period) ..

The folder 2017-05-17-mc_log_range and the file 2017-05-17-mc_log_range.tar.gz were deleted even if they don't match the pattern '*.xbstream' defined in the config file.

Am I doing something wrong ?

Timestamp-pattern no such group

Hi,

I want use timestamp-pattern option for extract timestamp from this above filename:

prometheus-grafana-production-09-04-2020.tar.gz

So i use this regex, that i have test on regex101.com :

r'''(?P<day>\d{2})-(?P<month>\d{2})-(?P<year>\d{4})'''

and their groups day, month and year are correcty captured in regex101.

So when i test on my backup server, rotate-backups says:

2020-04-09 16:14:22 host property_manager[26040] SPAM RotateBackups.timestamp_pattern reporting assigned or cached value (<_sre.SRE_Pattern object at 0x7f55def01df0>) ..
2020-04-09 16:14:22 host rotate_backups[26040] DEBUG Failed to match time stamp in filename: prometheus-grafana-production-09-04-2020.tar.gz
2020-04-09 16:14:22 host property_manager[26040] SPAM Location.ssh_alias reporting assigned or cached value (None) ..
2020-04-09 16:14:22 host property_manager[26040] SPAM Location.directory reporting assigned or cached value ('/home/backup/externalBackupTransfert/prometheus-grafana/production') ..
2020-04-09 16:14:22 host rotate_backups[26040] INFO No backups found in /home/backup/externalBackupTransfert/prometheus-grafana/production.

and in this directory i have a one file named; prometheus-grafana-production-09-04-2020.tar.gz

I don't understand for why this timestamp is not interpreted by rotate-backups, can you help me ?

ImportError on Linux: rotate-backups will not run

Hi,

I installed rotate-backups on my Fedora 31 with

pip install --user rotate-backups

but I get an ImportError when trying to run rotate-backups from the CLI, for example:

am@am:~$ rotate-backups -h
Traceback (most recent call last):
  File "/home/am/.local/bin/rotate-backups", line 5, in <module>
    from rotate_backups.cli import main
  File "/home/am/.local/lib/python3.7/site-packages/rotate_backups/cli.py", line 211, in <module>
    from coloredlogs import WINDOWS
ImportError: cannot import name 'WINDOWS' from 'coloredlogs' (/home/am/.local/lib/python3.7/site-packages/coloredlogs/__init__.py)

The problem seems to have been introduced by this commit 4 days ago.

If I open a python3 console, I can import coloredlogs without problem, but can't import WINDOWS from coloredlogs

am@am:~$ python3
Python 3.7.6 (default, Jan 30 2020, 09:44:41) 
[GCC 9.2.1 20190827 (Red Hat 9.2.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import coloredlogs
>>> from coloredlogs import WINDOWS
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: cannot import name 'WINDOWS' from 'coloredlogs' (/home/am/.local/lib/python3.7/site-packages/coloredlogs/__init__.py)

Redirect Output

Since we're using this script on a Synology Rackstation we'd like to redirect everything to a logfile.
But trying to redirect the stdout/stderr streams does not work.

It'd be nice to have an option for a log-file, e.g.
--log-file /var/log/myBackupRetention.log

`include` not working if used in ini-file?

Hi,

I have a directory with a lot of diverse backups, something like

a.20220202.121213.gz
a.20220203.121213.gz
a.20220204.121213.gz
b.20220202.121213.gz
b.20220203.121213.gz
b.20220204.121213.gz

This works fine if I specify the -I 'a*' on the command line, however, I would like to use it in an ini-file.

This does not work :(

[/backup-p]
include = a*
hourly = 24
daily = 7
ionice = idle

What is the correct way to do this using ini?

Fails to work on macOS

Your tool works flawlessly on my linux box, however I wanted to make use of it on my MacBook, but I run into a problem.

You call stat here, and that causes a problem on macOS, since the CLI is different here.

I'm not sure if this is the only issue preventing it from working. Is there maybe a way to get the operating system from the executor to try and prevent this issue?

Thanks for making this, btw

weekly not working correctly at start of year

It seems the weekly parameter is not working correctly at the start of the year.

I have these files:

2020-12-26
2021-01-02
2021-01-09
2021-01-16
2021-01-23
2021-01-30

Invoking rotate-backups with 4 weeks it skips 2021-01-09 and keeps 2021-01-02 instead. Expected behaviour was to keep 2021-01-09 and delete 2021-01-02.

Command
rotate-backups --weekly 4 ...

Output

rotate_backups[3695] INFO Found 6 timestamped backups in ...
rotate_backups[3695] INFO Deleting 2020-12-26 ..
rotate_backups[3695] INFO Preserving 2021-01-02 (matches 'weekly' retention period) ..
rotate_backups[3695] INFO Deleting 2021-01-09 ..
rotate_backups[3695] INFO Preserving 2021-01-16 (matches 'weekly' retention period) ..
rotate_backups[3695] INFO Preserving 2021-01-23 (matches 'weekly' retention period) ..
rotate_backups[3695] INFO Preserving 2021-01-30 (matches 'weekly' retention period) ..

Increasing to 5 weeks includes 2021-01-09 again.
Command
rotate-backups --weekly 5 ...

Output

2021-03-07 12:25:14 Tyrion rotate_backups[3702] INFO Found 6 timestamped backups in ...
2021-03-07 12:25:14 Tyrion rotate_backups[3702] INFO Deleting 2020-12-26 ..
2021-03-07 12:25:14 Tyrion rotate_backups[3702] INFO Preserving 2021-01-02 (matches 'weekly' retention period) ..
2021-03-07 12:25:14 Tyrion rotate_backups[3702] INFO Preserving 2021-01-09 (matches 'weekly' retention period) ..
2021-03-07 12:25:14 Tyrion rotate_backups[3702] INFO Preserving 2021-01-16 (matches 'weekly' retention period) ..
2021-03-07 12:25:14 Tyrion rotate_backups[3702] INFO Preserving 2021-01-23 (matches 'weekly' retention period) ..
2021-03-07 12:25:14 Tyrion rotate_backups[3702] INFO Preserving 2021-01-30 (matches 'weekly' retention period) ..

Using the latest version 8.1

(rotate_backups) ~/p/rotate_backups_test $ pip show rotate-backups
Name: rotate-backups
Version: 8.1
Summary: Simple command line interface for backup rotation
Home-page: https://github.com/xolox/python-rotate-backups
Author: Peter Odding
Author-email: [email protected]
License: MIT
Location: /home/cp/.venv/rotate_backups/lib/python3.8/site-packages
Requires: naturalsort, update-dotdee, python-dateutil, verboselogs, humanfriendly, six, property-manager, coloredlogs, executor, simpleeval
Required-by:

Install failed on Debian 9.5 - Python 2.7.13

root@db1:/home/debian# pip install rotate-backups
Collecting rotate-backups
  Using cached https://files.pythonhosted.org/packages/c2/65/9cd9c34c1f0a7d8671603779f49326fee9583556b9edf558ce7a2a34bd81/rotate_backups-6.0-py2.py3-none-any.whl
Collecting update-dotdee>=5.0 (from rotate-backups)
  Using cached https://files.pythonhosted.org/packages/b8/d2/2784de11083ddc5048bfee3d6159a1a9ca5a753ec16e6cd6243d22733155/update_dotdee-5.0-py2.py3-none-any.whl
Collecting property-manager>=2.3 (from rotate-backups)
  Using cached https://files.pythonhosted.org/packages/6f/b6/e6f254334a1a5c8cd0bd0f5b1c44ed7607952e91306b0f569940431897f2/property_manager-2.3.1-py2.py3-none-any.whl
Collecting verboselogs>=1.5 (from rotate-backups)
  Downloading https://files.pythonhosted.org/packages/b8/9d/c5c3cb0093642042fd604b53928ac65e7650fdc5a8a97814288e29beab84/verboselogs-1.7-py2.py3-none-any.whl
Collecting simpleeval>=0.8.7 (from rotate-backups)
  Using cached https://files.pythonhosted.org/packages/cd/1d/4890d49a61a08d71831aa3eef7c21d71ce6e2b200c711f2776d88ea486a0/simpleeval-0.9.6.tar.gz
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
    ImportError: No module named setuptools
    

Overwrite settings for folders in config file

I tried to use a construct like this:

[/mnt/backup/*/]
weekly = 4
monthly = 12
yearly = always
ionice = idle

[/mnt/backup/archive*/]
monthly = always

[/mnt/backup/archive2/]
weekly = 52

Each block generates folders for run, so construction above generate list with duplicates:

  1. /mnt/backup/archive1
  2. /mnt/backup/archive2
  3. /mnt/backup/arch
  4. /mnt/backup/archive1
  5. /mnt/backup/archive2
  6. /mnt/backup/archive2

But settings applies only from [/mnt/backup/*/] entry, order does not matter.

I suggest to implement ability to overwrite settings for folders.
 

Add a --version option

First of all, many thanks for this great script.

I would like to have a --version option, so that I can quickly check whether the script is out of date, or simply what version I am using now.

I am sure that there is some pip magic to get the installed package version, but I do not know Python at all. And I am guessing that you can install the script with some other means.

AttributeError in SysLogHandler

Hello and thank you for a useful package.
invoking 'python -v' gives me 'Python 3.5.4'.
invoking 'rotate-backups -h' gives me the help screen, but then:
Error in atexit._run_exitfuncs:
Traceback (most recent call last):
File "/usr/local/lib/python3.5/logging/init.py", line 1888, in shutdown
h.close()
File "/usr/local/lib/python3.5/logging/handlers.py", line 882, in close
self.socket.close()
AttributeError: 'SysLogHandler' object has no attribute 'socket'

(I got this with python 3.6, which according to your website is not supported, as well).
The package was installed via pip into a python:3.5-jessie docker image.

"ValueError: hour must be in 0..23" Error

I have a weird issue when running rotate-backups via CLI. The directory I have specified has backups from about 6 months ago; the CLI command looks like this -

rotate-backups -r -v --hourly=0 --include="*.tar.gz" --daily 3 --weekly 4 --monthly 3 --dry-run $PATH

The error I get is as follows -

 2018-07-31 18:06:51 142851cc0396 rotate_backups.cli[9] INFO Performing a dry run (because of --dry-run option) ..
 2018-07-31 18:06:51 142851cc0396 rotate_backups.cli[9] VERBOSE Rotation scheme defined on command line: {'monthly': 3, 'hourly': 0, 'daily': 7, 'weekly': 4}
 2018-07-31 18:06:51 142851cc0396 rotate_backups.cli[9] VERBOSE Selected 1 location based on command line arguments:
 2018-07-31 18:06:51 142851cc0396 rotate_backups.cli[9] VERBOSE  1. /rotate/dsquad/backups/test-rotate/cassandra
 2018-07-31 18:06:51 142851cc0396 rotate_backups[9] VERBOSE No configuration found for /rotate/dsquad/backups/test-rotate/cassandra.
 2018-07-31 18:06:51 142851cc0396 rotate_backups[9] INFO Scanning /rotate/dsquad/backups/test-rotate/cassandra for backups ..
 Traceback (most recent call last):
   File "/usr/bin/rotate-backups", line 11, in <module>
     sys.exit(main())
   File "/usr/lib/python2.7/site-packages/rotate_backups/cli.py", line 287, in main
     program.rotate_backups(location)
   File "/usr/lib/python2.7/site-packages/rotate_backups/__init__.py", line 442, in rotate_backups
     sorted_backups = self.collect_backups(location)
   File "/usr/lib/python2.7/site-packages/rotate_backups/__init__.py", line 540, in collect_backups
     timestamp=datetime.datetime(*(int(group, 10) for group in match.groups('0'))),
 ValueError: hour must be in 0..23

--move option

The paranoid among your happy users would appreciate a --move option, at least initially, to move files to a given directory rather than deleting them right away. I managed to hack this with --removal-command=, but needed to write a script, since my various attempts at escaping mv $1 /my/backup/dir didn’t seem to work.

Prevent littering the system log

First of all, many thanks for your excellent script.

The documentation states: "Log messages are saved to the system log"

Can you provide an option to stop littering the system log? I am running the script in an automated fashion, and I am keeping the console output in a separate place, in case I need to look at it.

Unfortunately, there does not seem to be way to stop rotate-backup from writing to the system log. That is a lot of unnecessary messages to ignore.

Provide alternative datetime extraction strategies

Hi,

I'm trying to rotate some files but I have some trouble given that the file names includes more digits than the date.

sxx-01-00.1.1build133003_2019-07-23_04:00:01.zip gets parsed to datetime(1330, 3, 20, 19, 7, 23)

I'm unable to save the files with other names since I don't manage the backups themselfs.
Is there a way I could give some insight to the script to understand my filenames?

It would be great if we could use a switch that use ctime or mtime instead of fuzzy matching the filename. Another way would be giving the user a way to specify a custom regexp to parse the file.
I thought of another way also, adding a custom 'wildcard' in the include pattern that says, match the date from that part of the file, eg: sxx-*_%datetime%.zip. It could also search for a date only where there is a wildcard char.

Thanks for building this awesome module, I used it in several other projects and it always worked as intended.

Best,

Preserving the most recent backups

Let's assume that I have two backups:

backup-2016-01-10_21-15-00.tar.gz
backup-2016-01-10_21-45-00.tar.gz

Then I do:

$ rotate-backups --hourly=1 --dry-run dir/ 
2016-08-03 12:44:41 tonyhost rotate_backups.cli[6556] INFO Performing a dry run (because of --dry-run option) ..
2016-08-03 12:44:41 tonyhost rotate_backups[6556] INFO Scanning /home/tonyo/tmp/backup_rotate/dir for backups ..
2016-08-03 12:44:41 tonyhost rotate_backups[6556] INFO Found 2 timestamped backups in /home/tonyo/tmp/backup_rotate/dir.
2016-08-03 12:44:41 tonyhost rotate_backups[6556] INFO Preserving ~/tmp/backup_rotate/dir/backup-2016-01-10_21-15-00.tar.gz (matches 'hourly' retention period) ..
2016-08-03 12:44:41 tonyhost rotate_backups[6556] INFO Deleting ~/tmp/backup_rotate/dir/backup-2016-01-10_21-45-00.tar.gz ..

So rotate-backups removes the second, more recent backup (..._21-45-...). Could you please clarify why we don't want to keep the most recent one, and remove the one made at 21:15?
Does it make sense to add some option (like --keep-recent) to change this behaviour and keep the latest backups?

As a general (and somehow related) question, is it planned to introduce a minutely option to enable rotation if backups are made several times per hour?

ensure_writable limits the applicability of this awesome script and should be removed

First, let me thank you for creating this nice script, which does exactly what I was looking for and so saved me from having to write something less fancy myself.

However, in order to make it work the way I needed it, I had to remove the ensure_writable() check.

I'm proposing that you might consider removing this check altogether and instead display a meaningful error message when the delete command actually fails and not before.
This script is, after all, intended for admins who should know what they are doing.
The check fails under certain conditions when the actual removal command would have performed perfectly fine.

In my case it wanted to give a --removal-command like e.g. sudo rm <very specific directorytree> or e.g. sudo zfs destroy <only certain sapshots> and NOT run the script using sudo or give the --use-sudo option, causing test or find to run as root, as there is no need for that. (It is good practice to limit sudo to one very specific command for that specific user running rotate-backups.)

This all would have worked perfectly out of the box (it does now), except for the overzealous ensure_writable() check at the beginning, which, though well intended, is actually not helpful and prohibits using the script in a safe manner.

Look here if you're looking for an alternative that's actively developed

Hi all,

I noticed this tool is no longer maintained and decided create a fresh alternative, drawing inspiration from the exceptional work of @xolox. You can explore and contribute to the project here. Please note that my intention is not to undermine or diminish this original project in any way. I'm just inform others that a viable, actively developed alternative now exists.

@xolox - if you're still around and don't like this post, it won't hurt my feelings if you delete this.

Cheers

Rotation policy not work correctly.

root@odoo-backup-1549444260-2jshc:/# pip show rotate-backups
Name: rotate-backups
Version: 4.3
Summary: Simple command line interface for backup rotation
Home-page: https://github.com/xolox/python-rotate-backups
Author: Peter Odding
Author-email: [email protected]
License: UNKNOWN
Location: /usr/local/lib/python3.6/dist-packages
Requires: humanfriendly, naturalsort, six, simpleeval, verboselogs, property-manager, executor, python-dateutil, coloredlogs
Required-by: rotate-backups-s3
root@odoo-backup-1549444260-2jshc:/# date
Wed Feb  6 12:20:42 +03 2019
root@odoo-backup-1549444260-2jshc:/# rotate-backups --dry-run --minutely=5 --hourly=24*4 --daily=7 --weekly=8 --monthly=12 --yearly=3 --include=*.tag --include=*.zip --relaxed ${backups_path}
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups.cli[103] INFO Performing a dry run (because of --dry-run option) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Scanning /backups for backups ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Found 32 timestamped backups in /backups.
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2018-11-20_15-57-53.zip (matches 'hourly', 'weekly', 'monthly' and 'yearly' retention periods) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2018-11-26_00-47-48.zip (matches 'hourly' and 'weekly' retention periods) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2018-12-01_00-12-59.zip (matches 'hourly' and 'monthly' retention periods) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2018-12-12_21-30-46.zip (matches 'hourly' and 'weekly' retention periods) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2018-12-17_09-40-02.zip (matches 'hourly' and 'weekly' retention periods) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2019-01-09_11-57-56.tag (matches 'hourly', 'weekly', 'monthly' and 'yearly' retention periods) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2019-01-12_00-53-23.tag (matches 'hourly' and 'daily' retention periods) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2019-01-13_01-22-35.tag (matches 'hourly' and 'daily' retention periods) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2019-01-14_00-25-51.tag (matches 'hourly', 'daily' and 'weekly' retention periods) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2019-01-15_01-01-30.tag (matches 'hourly' and 'daily' retention periods) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2019-01-16_00-21-27.tag (matches 'hourly' and 'daily' retention periods) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2019-02-05_13-56-08.tag (matches 'hourly', 'daily', 'weekly' and 'monthly' retention periods) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2019-02-06_00-56-08.tag (matches 'hourly' and 'daily' retention periods) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2019-02-06_01-56-12.tag (matches 'hourly' retention period) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2019-02-06_02-56-05.tag (matches 'hourly' retention period) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2019-02-06_03-56-08.tag (matches 'hourly' retention period) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2019-02-06_04-56-13.tag (matches 'hourly' retention period) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2019-02-06_05-56-06.tag (matches 'hourly' retention period) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2019-02-06_07-56-13.tag (matches 'hourly' retention period) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Deleting /backups/odoo_devops_2019-02-06_07-56-13.zip ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2019-02-06_08-56-07.tag (matches 'hourly' retention period) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Deleting /backups/odoo_devops_2019-02-06_08-56-07.zip ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2019-02-06_09-56-11.tag (matches 'minutely' and 'hourly' retention periods) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Deleting /backups/odoo_devops_2019-02-06_09-56-11.zip ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2019-02-06_10-46-05.tag (matches 'minutely' and 'hourly' retention periods) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Deleting /backups/odoo_devops_2019-02-06_10-46-05.zip ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2019-02-06_11-03-07.tag (matches 'minutely' and 'hourly' retention periods) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Deleting /backups/odoo_devops_2019-02-06_11-03-07.zip ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2019-02-06_11-37-10.tag (matches 'minutely' retention period) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Deleting /backups/odoo_devops_2019-02-06_11-37-10.zip ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Preserving /backups/odoo_devops_2019-02-06_11-54-10.tag (matches 'minutely' retention period) ..
2019-02-06 12:20:59 odoo-backup-1549444260-2jshc rotate_backups[103] INFO Deleting /backups/odoo_devops_2019-02-06_11-54-10.zip ..

As you can see the *.tag and *.zip files have one rotation policy, but in 2019 year rotated different in fact.

The `--daily` option inexplicably(?) deletes yesterday's backup unless `-p` is also given

I read through the other issues (again), but I'm seeing daily backups deleted right before my eyes with the --daily= switch, and I'm still not sure if this is the intended behavior.

The gist of the problem is --daily=n (n > 1) without --prefer-recent seems to delete backups that occurred within the last 24 hours, which never allows enough of them to stick around for weekly, yearly, or monthly backups to be retained.

Here's the setup:

  • CentOS 6, Python 2.7.something (from the RedHat SCL), rotate-backups version 4.4

  • a nightly cron job which invokes rotate-backups (at 12:39 am, if that matters) with these options

      --quiet \
      --prefer-recent \
      --daily=$KEEP_DAYS \
      --weekly=$KEEP_WEEKS \
      --monthly=$KEEP_MONTHS \
      --yearly=$KEEP_YEARS \
    

    (where those variables have values of 7, 4, 6, and 2, respectively)

  • the cron job runs rotate-backup before creating a tar.bz2 backup of some files, and a .sql.gz database dump, both of which get a timestamp that looks like

      sqldump=owncloud-sqldump_$(date +%Y-%m-%d).sql
    

    (i.e., ISO 8601 format)

  • the tar.bz2 files seem to be properly rotated, but the sql.gz one is just deleted every night, so when I go check on rotate-backups's handiwork, there's just the one that finished at around 1 am, and no others.

I'm not sore about the files being deleted (there are ample warnings in the documentation about using --dry-run), but I'm puzzled by this behavior.

Here are two invocations of rotate-backups with the same options, except the second one adds --prefer-recent:

[owncloud_backups]$ rotate-backups --dry-run --daily=7 --weekly=4 --monthly=6 --yearly=2 .
<snipped> rotate_backups.cli[19231] INFO Performing a dry run (because of --dry-run option) ..
<snipped> rotate_backups[19231] INFO Scanning /var/adm/owncloud_backups for backups ..
<snipped> rotate_backups[19231] INFO Found 12 timestamped backups in /var/adm/owncloud_backups.
<snipped> rotate_backups[19231] INFO Preserving /var/adm/owncloud_backups/owncloud-data_2017-06-06.tar.bz2 (matches 'monthly' and 'yearly' retention periods) ..
<snipped> rotate_backups[19231] INFO Preserving /var/adm/owncloud_backups/owncloud-data_2017-09-22.tar.bz2 (matches 'weekly' and 'monthly' retention periods) ..
<snipped> rotate_backups[19231] INFO Preserving /var/adm/owncloud_backups/owncloud-data_2017-09-25.tar.bz2 (matches 'weekly' retention period) ..
<snipped> rotate_backups[19231] INFO Deleting /var/adm/owncloud_backups/owncloud-data_2017-09-28.tar.bz2 ..
<snipped> rotate_backups[19231] INFO Preserving /var/adm/owncloud_backups/owncloud-data_2017-09-29.tar.bz2 (matches 'daily' retention period) ..
<snipped> rotate_backups[19231] INFO Preserving /var/adm/owncloud_backups/owncloud-data_2017-09-30.tar.bz2 (matches 'daily' retention period) ..
<snipped> rotate_backups[19231] INFO Preserving /var/adm/owncloud_backups/owncloud-data_2017-10-01.tar.bz2 (matches 'daily' and 'monthly' retention periods) ..
<snipped> rotate_backups[19231] INFO Preserving /var/adm/owncloud_backups/owncloud-data_2017-10-02.tar.bz2 (matches 'daily' and 'weekly' retention periods) ..
<snipped> rotate_backups[19231] INFO Preserving /var/adm/owncloud_backups/owncloud-data_2017-10-03.tar.bz2 (matches 'daily' retention period) ..
<snipped> rotate_backups[19231] INFO Preserving /var/adm/owncloud_backups/owncloud-data_2017-10-04.tar.bz2 (matches 'daily' retention period) ..
<snipped> rotate_backups[19231] INFO Preserving /var/adm/owncloud_backups/owncloud-data_2017-10-05.tar.bz2 (matches 'daily' retention period) ..
<snipped> rotate_backups[19231] INFO Deleting /var/adm/owncloud_backups/owncloud-sqldump_2017-10-05.sql.gz ..

versus

[owncloud_backups]$ rotate-backups --dry-run --prefer-recent --daily=7 --weekly=4 --monthly=6 --yearly=2 .
<snipped> Performing a dry run (because of --dry-run option) ..
<snipped> Scanning /path/to/owncloud_backups for backups ..
<snipped> Found 12 timestamped backups in /path/to.
<snipped> Preserving /path/to/owncloud-data_2017-06-06.tar.bz2 (matches 'monthly' retention period) ..
<snipped> Preserving /path/to/owncloud-data_2017-09-22.tar.bz2 (matches 'weekly' retention period) ..
<snipped> Deleting /path/to/owncloud-data_2017-09-25.tar.bz2 ..
<snipped> Deleting /path/to/owncloud-data_2017-09-28.tar.bz2 ..
<snipped> Preserving /path/to/owncloud-data_2017-09-29.tar.bz2 (matches 'daily' retention period) ..
<snipped> Preserving /path/to/owncloud-data_2017-09-30.tar.bz2 (matches 'daily' and 'monthly' retention periods) ..
<snipped> Preserving /path/to/owncloud-data_2017-10-01.tar.bz2 (matches 'daily' and 'weekly' retention periods) ..
<snipped> Preserving /path/to/owncloud-data_2017-10-02.tar.bz2 (matches 'daily' retention period) ..
<snipped> Preserving /path/to/owncloud-data_2017-10-03.tar.bz2 (matches 'daily' retention period) ..
<snipped> Preserving /path/to/owncloud-data_2017-10-04.tar.bz2 (matches 'daily' retention period) ..
<snipped> Deleting /path/to/owncloud-data_2017-10-05.tar.bz2 ..
<snipped> Preserving /path/to/owncloud-sqldump_2017-10-05.sql.gz (matches 'daily', 'weekly', 'monthly' and 'yearly' retention periods) ..

Why does it seem to be working fine for the .tar.bz files?

Is this totally sensible behavior and my brain just can't grasp how the default algorithm works? Or is this errant behavior introduced recently, by a recent PR or something? I feel like the current behavior is contrary to the usual connotation of "daily backups" or "keep 7 days of daily backups."

Bug: hourly=0 preserves one hourly backup

Cheers,

this is a bug report:

Expected Behavior
When one runs rotate-backups with flag hourly=0, all hourly backups will be removed.

Observed Behavior
When one runs rotate-backups with flag hourly=0, one hourly backup will be preserved. As you can see from the log below, this missbehavior does not occur for daily=0.

rotate-backups --hourly=0 --daily=0 --weekly=4 --monthly=12 --yearly=always --dry-run /archive
sh: --daily=0: unknown operand
INFO Performing a dry run (because of --dry-run option) ..
INFO Scanning /archive for backups ..
INFO Found 20 timestamped backups in /archive.
INFO Preserving /archive/backup-2021-08-11T21-30-33.tar.gz (matches 'monthly' and 'yearly' retention periods) ..
INFO Preserving /archive/backup-2021-10-16T21-20-05.tar.gz (matches 'monthly' retention period) ..
INFO Preserving /archive/backup-2021-11-01T03-00-07.tar.gz (matches 'monthly' retention period) ..
INFO Preserving /archive/backup-2021-12-01T03-00-07.tar.gz (matches 'monthly' retention period) ..
INFO Deleting /archive/backup-2021-12-20T03-00-07.tar.gz ..
INFO Preserving /archive/backup-2021-12-27T03-00-08.tar.gz (matches 'weekly' retention period) ..
INFO Preserving /archive/backup-2022-01-01T12-00-08.tar.gz (matches 'weekly', 'monthly' and 'yearly' retention periods) ..
INFO Deleting /archive/backup-2022-01-01T16-00-07.tar.gz ..
INFO Preserving /archive/backup-2022-01-03T03-00-07.tar.gz (matches 'weekly' retention period) ..
INFO Deleting /archive/backup-2022-01-04T03-00-08.tar.gz ..
INFO Deleting /archive/backup-2022-01-05T00-00-07.tar.gz ..
INFO Deleting /archive/backup-2022-01-06T00-00-07.tar.gz ..
INFO Deleting /archive/backup-2022-01-07T00-00-07.tar.gz ..
INFO Deleting /archive/backup-2022-01-08T03-00-07.tar.gz ..
INFO Deleting /archive/backup-2022-01-09T03-00-07.tar.gz ..
INFO Preserving /archive/backup-2022-01-10T00-00-07.tar.gz (matches 'weekly' retention period) ..
INFO Deleting /archive/backup-2022-01-11T00-00-08.tar.gz ..
INFO Deleting /archive/backup-2022-01-12T00-00-08.tar.gz ..
INFO Deleting /archive/backup-2022-01-13T00-00-08.tar.gz ..
INFO Preserving /archive/backup-2022-01-13T09-01-49.tar.gz (matches 'hourly' retention period) ..

Interestingly, the script echos sh: --daily=0: unknown operand.

Further, if hourly=-1, all hourly backups will be removed:

rotate-backups --hourly=-1 --daily=0 --weekly=4 --monthly=12 --yearly=always --dry-run /archive
INFO Performing a dry run (because of --dry-run option) ..
INFO Scanning /archive for backups ..
INFO Found 20 timestamped backups in /archive.
INFO Preserving /archive/backup-2021-08-11T21-30-33.tar.gz (matches 'monthly' and 'yearly' retention periods) ..
INFO Preserving /archive/backup-2021-10-16T21-20-05.tar.gz (matches 'monthly' retention period) ..
INFO Preserving /archive/backup-2021-11-01T03-00-07.tar.gz (matches 'monthly' retention period) ..
INFO Preserving /archive/backup-2021-12-01T03-00-07.tar.gz (matches 'monthly' retention period) ..
INFO Deleting /archive/backup-2021-12-20T03-00-07.tar.gz ..
INFO Preserving /archive/backup-2021-12-27T03-00-08.tar.gz (matches 'weekly' retention period) ..
INFO Preserving /archive/backup-2022-01-01T12-00-08.tar.gz (matches 'weekly', 'monthly' and 'yearly' retention periods) ..
INFO Deleting /archive/backup-2022-01-01T16-00-07.tar.gz ..
INFO Preserving /archive/backup-2022-01-03T03-00-07.tar.gz (matches 'weekly' retention period) ..
INFO Deleting /archive/backup-2022-01-04T03-00-08.tar.gz ..
INFO Deleting /archive/backup-2022-01-05T00-00-07.tar.gz ..
INFO Deleting /archive/backup-2022-01-06T00-00-07.tar.gz ..
INFO Deleting /archive/backup-2022-01-07T00-00-07.tar.gz ..
INFO Deleting /archive/backup-2022-01-08T03-00-07.tar.gz ..
INFO Deleting /archive/backup-2022-01-09T03-00-07.tar.gz ..
INFO Preserving /archive/backup-2022-01-10T00-00-07.tar.gz (matches 'weekly' retention period) ..
INFO Deleting /archive/backup-2022-01-11T00-00-08.tar.gz ..
INFO Deleting /archive/backup-2022-01-12T00-00-08.tar.gz ..
INFO Deleting /archive/backup-2022-01-13T00-00-08.tar.gz ..
INFO Deleting /archive/backup-2022-01-13T09-01-49.tar.gz ..

Regards,
Jan

Support for string month representation in filenames

Hi,

Could you consider adding in support for backups which use string representation for months instead of numeric pls? Would be a good option eg Jan, Feb, Mar etc. Some vendors sadly do use this format for backups.

Thanks

PC

strange fuzzy results, deletes what should be preserved

Well can't get the point why, but it's happening.

[root@db4 galera_backup]# cat /etc/rotate-backups.ini 
[/galera_backup]
hourly = 3
daily = 1
weekly = 0
monthly = 0
yearly = 0
ionice = idle

Mind the "Deleting directory" in the second example. The only change is in "hour" in "timestamp".

[root@db4 galera_backup]# /bin/rotate-backups -nv -x '*'$(date  --date="1 days ago" "+%Y-%m-%d")'*'
2016-03-17 12:54:35 db4.sl.example.lab rotate_backups.cli[36454] INFO Performing a dry run (because of -n option) ..
2016-03-17 12:54:35 db4.sl.example.lab rotate_backups[36454] DEBUG Using configuration file /etc/rotate-backups.ini ..
2016-03-17 12:54:35 db4.sl.example.lab rotate_backups[36454] DEBUG Using configuration file /etc/rotate-backups.ini ..
2016-03-17 12:54:35 db4.sl.example.lab rotate_backups[36454] INFO Scanning directory for backups: /galera_backup
2016-03-17 12:54:35 db4.sl.example.lab rotate_backups[36454] DEBUG Excluded 'galera_backup_db4.sl.example.lab_2016-03-16_12-00' (it matched the exclude list).
2016-03-17 12:54:35 db4.sl.example.lab rotate_backups[36454] DEBUG Excluded 'galera_backup_db4.sl.example.lab_2016-03-16_22-00' (it matched the exclude list).
2016-03-17 12:54:35 db4.sl.example.lab rotate_backups[36454] INFO Found 3 timestamped backups in /galera_backup.
2016-03-17 12:54:35 db4.sl.example.lab rotate_backups[36454] INFO Preserving /galera_backup/galera_backup_db4.sl.example.lab_2016-03-17_12-00 (matches 'daily' retention period) ..
2016-03-17 12:54:35 db4.sl.example.lab rotate_backups[36454] INFO Preserving /galera_backup/galera_backup_db4.sl.example.lab_2016-03-17_16-00 (matches 'hourly' retention period) ..
2016-03-17 12:54:35 db4.sl.example.lab rotate_backups[36454] INFO Preserving /galera_backup/galera_backup_db4.sl.example.lab_2016-03-17_18-00 (matches 'hourly' retention period) ..
2016-03-17 12:54:35 db4.sl.example.lab rotate_backups[36454] INFO Nothing to do! (all backups preserved)
[root@db4 galera_backup]# 

[root@db4 galera_backup]# date
Thu Mar 17 12:57:34 CET 2016

[root@db4 galera_backup]# mv /galera_backup/galera_backup_db4.sl.example.lab_2016-03-17_18-00 /galera_backup/galera_backup_db4.sl.example.lab_2016-03-17_10-00


[root@db4 galera_backup]# /bin/rotate-backups -nv -x '*'$(date  --date="1 days ago" "+%Y-%m-%d")'*'
2016-03-17 12:58:01 db4.sl.example.lab rotate_backups.cli[37843] INFO Performing a dry run (because of -n option) ..
2016-03-17 12:58:01 db4.sl.example.lab rotate_backups[37843] DEBUG Using configuration file /etc/rotate-backups.ini ..
2016-03-17 12:58:01 db4.sl.example.lab rotate_backups[37843] DEBUG Using configuration file /etc/rotate-backups.ini ..
2016-03-17 12:58:01 db4.sl.example.lab rotate_backups[37843] INFO Scanning directory for backups: /galera_backup
2016-03-17 12:58:01 db4.sl.example.lab rotate_backups[37843] DEBUG Excluded 'galera_backup_db4.sl.example.lab_2016-03-16_12-00' (it matched the exclude list).
2016-03-17 12:58:01 db4.sl.example.lab rotate_backups[37843] DEBUG Excluded 'galera_backup_db4.sl.example.lab_2016-03-16_22-00' (it matched the exclude list).
2016-03-17 12:58:01 db4.sl.example.lab rotate_backups[37843] INFO Found 3 timestamped backups in /galera_backup.
2016-03-17 12:58:01 db4.sl.example.lab rotate_backups[37843] INFO Preserving /galera_backup/galera_backup_db4.sl.example.lab_2016-03-17_10-00 (matches 'daily' retention period) ..
2016-03-17 12:58:01 db4.sl.example.lab rotate_backups[37843] INFO Deleting directory /galera_backup/galera_backup_db4.sl.example.lab_2016-03-17_12-00 ..
2016-03-17 12:58:01 db4.sl.example.lab rotate_backups[37843] INFO Preserving /galera_backup/galera_backup_db4.sl.example.lab_2016-03-17_16-00 (matches 'hourly' retention period) ..


# root owned are created manually, for testing
# the 11-00-00 was added later
#
[root@db4 galera_backup]# ll
total 12
drwxr-xr-x 6 mysql mysql 4096 Mar 16 00:08 galera_backup_db4.sl.example.lab_2016-03-16_12-00
drwxr-xr-x 6 mysql mysql 4096 Mar 16 20:33 galera_backup_db4.sl.example.lab_2016-03-16_22-00
drwxr-xr-x 2 root  root     6 Mar 17 12:54 galera_backup_db4.sl.example.lab_2016-03-17_10-00
drwxr-xr-x 2 root  root     6 Mar 17 12:59 galera_backup_db4.sl.example.lab_2016-03-17_11-00-00
drwxr-xr-x 6 mysql mysql 4096 Mar 16 20:33 galera_backup_db4.sl.example.lab_2016-03-17_12-00
drwxr-xr-x 2 root  root     6 Mar 17 12:54 galera_backup_db4.sl.example.lab_2016-03-17_16-00


SSH remote directory not accessible

Hi,

I've just discovered this great tool and tested it on a local directory. As said in the documentation, rotate-backups handles remote directory over SSH.
I've tried it creating a new configuration file as follow:

[ssh.cluster023.x.x.x:/backups]
ssh-user=example
daily=1
weekly=7
monthly=4
yearly=12

Unfortunately, the script throws an error which I don't understand:

 rotate-backups  --dry-run -y 2 -m 12 -w 8 -p -d 8  ssh.cluter023.x.x.x:/bakcups/ 2>&1
2018-12-03 17:20:19 local rotate_backups.cli[24974] INFO Performing a dry run (because of --dry-run option) ..
2018-12-03 17:20:19 local rotate_backups[24974] INFO Scanning ssh.cluter023.x.x.x:/backups for backups ..
Traceback (most recent call last):
  File "/Users/tuco/src/workspace/rotate-backups/bin/rotate-backups", line 11, in <module>
    load_entry_point('rotate-backups==6.0', 'console_scripts', 'rotate-backups')()
  File "/Users/tuco/src/workspace/rotate-backups/lib/python2.7/site-packages/rotate_backups/cli.py", line 303, in main
    program.rotate_backups(location)
  File "/Users/tuco/src/workspace/rotate-backups/lib/python2.7/site-packages/rotate_backups/__init__.py", line 467, in rotate_backups
    sorted_backups = self.collect_backups(location)
  File "/Users/tuco/src/workspace/rotate-backups/lib/python2.7/site-packages/rotate_backups/__init__.py", line 559, in collect_backups
    location.ensure_readable()
  File "/Users/tuco/src/workspace/rotate-backups/lib/python2.7/site-packages/rotate_backups/__init__.py", line 748, in ensure_readable
    self.ensure_exists()
  File "/Users/tuco/src/workspace/rotate-backups/lib/python2.7/site-packages/rotate_backups/__init__.py", line 744, in ensure_exists
    """, location=self))
ValueError: The directory ssh.cluter023.x.x.x:/backups/ isn't accessible, most likely because it doesn't exist or because of permissions. If you're sure the directory exists you can use the --use-sudo option.

Is there something misconfigured of I did wrong??
For info, the hostname I try to connect to starts with ssh could it be the problem?

Thanks for your reply

Emmanuel

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.