Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
6787c65
skip lost+found directories
mathiasertl Jul 21, 2013
e67198c
pep8 cleanup
mathiasertl Jul 21, 2013
718bd79
massive pep8 cleanup
mathiasertl Jul 21, 2013
b76cbbf
add error function shortcut
mathiasertl Jul 21, 2013
7ac944a
update README
mathiasertl Jul 21, 2013
ec4d6cd
add support for the 'last' config value (always keep the last N backups)
mathiasertl Jul 21, 2013
313e5fd
use more telling time-tuple members
mathiasertl Jul 21, 2013
1c66751
cleanup
mathiasertl Jul 21, 2013
5b4b93c
continue when we can't parse timestamp
mathiasertl Jul 21, 2013
394f03e
add ChangeLog
mathiasertl Jul 21, 2013
a4c9fe0
pep8 cleanup
mathiasertl Jul 21, 2013
8c3f383
add ChangeLog for dbdump
mathiasertl Jul 21, 2013
ad1bc93
Merge branch 'master' into debian
mathiasertl Jul 21, 2013
d81e756
update changelog
mathiasertl Jul 21, 2013
9f47ccc
update cron.d file for dbclean
mathiasertl Jul 21, 2013
db88bce
update repository URL
mathiasertl May 3, 2014
1b166fc
use plain-text dumps instead of binary dumps
mathiasertl Dec 7, 2014
1e8d9f6
do not redefine cmd
mathiasertl Dec 7, 2014
f43e7e9
rework ssh code to gzip before encryption and going over the wire, en…
mathiasertl Dec 7, 2014
d210c03
fix typo
mathiasertl Dec 7, 2014
fe8038f
add .gz before .gpg, use os.path.join
mathiasertl Dec 7, 2014
97cdf8c
call abspath
mathiasertl Dec 7, 2014
c996eff
rework local part to compress first
mathiasertl Dec 7, 2014
e54d9b6
update changelog, control
mathiasertl Dec 7, 2014
357c222
Merge branch 'master' into debian
mathiasertl Dec 7, 2014
9df8690
increase compat
mathiasertl Dec 7, 2014
cc1de22
update copyright file
mathiasertl Dec 7, 2014
9d00475
execute commands as database users in a login shell
Astranox Feb 12, 2015
2e374f9
fix --version parameter
mathiasertl Feb 14, 2016
098fdea
use tw=99
mathiasertl Feb 14, 2016
9b869b1
tw=99
mathiasertl Feb 14, 2016
90a436f
cosmetics
mathiasertl Aug 12, 2016
1038642
add pyenv version
mathiasertl Jan 20, 2019
d42544e
fix spaces
mathiasertl Jan 20, 2019
1bf4301
add dev reqs
mathiasertl Jan 20, 2019
fc43436
add tox.ini
mathiasertl Jan 20, 2019
04aa873
udpate style, unify header
mathiasertl Jan 20, 2019
8a81250
various style improvs
mathiasertl Jan 20, 2019
f1711c3
switch to sha256 checksums
mathiasertl Jan 20, 2019
c40a921
minor style
mathiasertl Jan 20, 2019
2fd0c11
isort cleanup
mathiasertl Jan 20, 2019
aa1736d
Update dbdump.py
somenet Jul 16, 2019
95ed31a
Merge pull request #3 from somenet/silence-deprecation-warning
mathiasertl Jul 20, 2019
53970a1
Merge pull request #1 from Astranox/master
mathiasertl Jul 20, 2019
51b1f67
Merge branch 'master' into debian
Astranox Jul 20, 2019
f1f8df2
update changelog
Astranox Jul 20, 2019
16da19b
add rename as build dependency
Astranox Jul 20, 2019
b9f5433
add --no-timeout option to ejabberdctl
mathiasertl Oct 24, 2019
957cb74
Merge branch 'master' of github.com:mathiasertl/db-backup
mathiasertl Oct 24, 2019
acd86c9
ignore .tox
mathiasertl Oct 24, 2019
b5d5f55
add py38
mathiasertl Oct 24, 2019
ab36e52
style
mathiasertl Oct 24, 2019
a34ea7a
update reqs
mathiasertl Oct 24, 2019
ea1935b
fix DeprecationWarning for SafeConfigParser in dbclean too
Astranox Jul 22, 2019
0b1d11e
use connectstring for connecting to databases
Astranox Dec 20, 2020
7ce1fad
add borg backup support
Astranox Nov 8, 2023
a021b95
Merge branch 'master' into debian
Astranox Nov 8, 2023
5805b9d
update changelog
Astranox Sep 8, 2021
2458c04
update debian/control to debhelper v10
Astranox Sep 8, 2021
4405236
add dh-python to control
Astranox Sep 8, 2021
6582d09
update debian-compat to 10
Astranox Sep 8, 2021
4585b3e
update changelog
Astranox Nov 8, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
*.pyc
*.swp
/.tox/
2 changes: 2 additions & 0 deletions .pep8
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pep8]
max-line-length = 99
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.6.6
9 changes: 9 additions & 0 deletions dbclean/ChangeLog
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
2016-02-14:
* Fix --version parameter.
* Use a text-width of 99 chars.

2013-07-21:
* Massive pep8 cleanup
* Update documentation (remove old --section parameter)
* Add this ChangeLog
* Add the "last" option, dbclean will always keep the given last backups.
6 changes: 4 additions & 2 deletions dbclean/README
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ are matched to those of dbdump, so they work together.

=== Installation ===
Simply check out the git-repository:
git clone http://git.git.fsinf.at/fsinf/db-backup.git

git clone https://github.com/mathiasertl/db-backup.git

If you don't want to specify the full path, you can of course copy dbclean.py
somewhere in your path (/usr/local/bin is usually good).

Expand All @@ -22,5 +24,5 @@ A sample configuration file is included with the source code, see:
dbclean.conf.example

After that, you can start the script with
dbclean.py --section=example
dbclean.py example
where example is the section in your config-file.
2 changes: 2 additions & 0 deletions dbclean/dbclean.conf.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
# sections.
#hourly = 72
#daily = 31
# Always keep the last three backups
#last = 3
#
# NOTE: You can also use the interpolation feature provided by the
# ConfigParser python module. The following line is used in the
Expand Down
203 changes: 102 additions & 101 deletions dbclean/dbclean.py
Original file line number Diff line number Diff line change
@@ -1,179 +1,180 @@
#!/usr/bin/env python3

"""
This program is designed to clean files from a specified directory.
The program is designed to work together with dbdump.py, so it is
basically designed to clean out regular database dumps. The files
are kept at a certain granularity (so daily backups will be kept
for a month, monthly backups for a year, etc.
Please see the README file for how to use this script and supported
features. You might also try calling this program with '--help'.

Copyright 2009 Mathias Ertl

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""

import os, time, sys, calendar, configparser, argparse

config_file = ['/etc/dbclean/dbclean.conf', os.path.expanduser('~/.dbclean.conf')]

parser = argparse.ArgumentParser(version="%prog 1.0",
description = """Cleanup regular database dumps created by dbdump. This script keeps backups
at given intervals for a given amount of time.""")
parser.add_argument('-c', '--config', type=str, dest='config', action='append', default=config_file,
help="""Additional config-files to use (default: %(default)s). Can be given multiple times
to name multiple config-files.""")
parser.add_argument('section', action='store', type=str,
help="Section in the config-file to use." )
#
# This program is designed to clean files from a specified directory. The program is designed to
# work together with dbdump.py, so it is basically designed to clean out regular database dumps.
# The files are kept at a certain granularity (so daily backups will be kept for a month, monthly
# backups for a year, etc. Please see the README file for how to use this script and supported
# features. You might also try calling this program with '--help'.
#
# Copyright 2009 - 2019 Mathias Ertl <[email protected]>
#
# This program is free software: you can redistribute it and/or modify it under the terms of the
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import argparse
import calendar
import configparser
import os
import sys
import time


def err(msg, *args):
print(msg % args, file=sys.stderr)


config_file = [
'/etc/dbclean/dbclean.conf',
os.path.expanduser('~/.dbclean.conf')
]

parser = argparse.ArgumentParser(
description="""Cleanup regular database dumps created by dbdump. This script keeps backups at
given intervals for a given amount of time.""")
parser.add_argument('--version', action='version', version='%(prog)s 1.1')
parser.add_argument(
'-c', '--config', type=str, dest='config', action='append',
default=config_file, help="""Additional config-files to use (default: %(default)s). Can be
given multiple times to name multiple config-files.""")
parser.add_argument('section', action='store', type=str, help="Section in the config-file to use.")
args = parser.parse_args()

if args.section=='DEFAULT':
if args.section == 'DEFAULT':
parser.error("--section must not be 'DEFAULT'.")

config = configparser.SafeConfigParser({
config = configparser.ConfigParser({
'format': '%%Y-%%m-%%d_%%H:%%M:%%S',
'hourly': '24', 'daily': '31',
'monthly': '12', 'yearly': '3'
'monthly': '12', 'yearly': '3',
'last': '3',
})
if not config.read(args.config):
parser.error("No config-files could be read.")

# check validity of config-file:
if args.section not in config:
print("Error: %s: No section found with that name." % args.section, file=sys.stderr)
err("Error: %s: No section found with that name.", args.section)
sys.exit(1)
if 'datadir' not in config[args.section]:
print("Error: %s: Section does not contain option 'datadir'." % args.section, file=sys.stderr)
err("Error: %s: Section does not contain option 'datadir'.", args.section)
sys.exit(1)

# get directory containing backups:
datadir = config.get(args.section, 'datadir')

# check that given directory exists and is a directory:
if not os.path.exists(datadir):
print("Error: %s: No such directory." % (datadir), file=sys.stderr)
err("Error: %s: No such directory.", datadir)
sys.exit(1)
elif not os.path.isdir(datadir):
print("Error: %s: Not a directory." % (datadir), file=sys.stderr)
err("Error: %s: Not a directory.", datadir)
sys.exit(1)

timeformat = config[args.section]['format']
hourly = int(config[args.section]['hourly'])
daily = int(config[args.section]['daily'])
monthly = int(config[args.section]['monthly'])
yearly = int(config[args.section]['yearly'])
last = int(config[args.section]['last'])
now = time.time()


class backup():
files = []

def __init__( self, time, base, file ):
def __init__(self, time, base, file):
self.time = time
self.base = base
self.files = [ file ]
self.files = [file]

def add( self, file ):
self.files.append( file )
def add(self, file):
self.files.append(file)

def is_daily( self ):
if self.time[3] == 0:
def is_daily(self):
if self.time.tm_hour == 0:
return True
else:
return False

def is_monthly( self ):
if self.is_daily() and self.time[2] == 1:
def is_monthly(self):
if self.is_daily() and self.time.tm_mday == 1:
return True
else:
return False

def is_yearly( self ):
if self.is_monthly() and self.time[1] == 1:
def is_yearly(self):
if self.is_monthly() and self.time.tm_mon == 1:
return True
else:
return False

def remove( self ):
def remove(self):
for file in self.files:
os.remove( file )
os.remove(file)

def __str__( self ):
return "%s in %s" %(self.files, self.base)
def __str__(self):
return "%s in %s" % (self.files, self.base)

now = time.time()

# loop through each dir in datadir
for dir in os.listdir( datadir ):
if dir.startswith( '.' ):
for dir in os.listdir(datadir):
if dir.startswith('.'):
# skip hidden directories
continue
if dir == 'lost+found':
continue

fullpath = os.path.normpath( datadir + '/' + dir )
if not os.path.isdir( fullpath ):
print( "Warning: %s: Not a directory." % (fullpath) )
fullpath = os.path.normpath(datadir + '/' + dir)
if not os.path.isdir(fullpath):
print("Warning: %s: Not a directory." % fullpath)
continue
os.chdir( fullpath )
os.chdir(fullpath)

backups = {}

files = os.listdir( '.' )
files = os.listdir('.')
files.sort()

for file in files:
filestamp = file.split( '.' )[0]
filestamp = file.split('.')[0]
timestamp = ''
try:
timestamp = time.strptime( filestamp, timeformat )
timestamp = time.strptime(filestamp, timeformat)
except ValueError as e:
print( '%s: %s' %(file, e) )
print('%s: %s' % (file, e))
continue

if timestamp not in list( backups.keys() ):
backups[timestamp] = backup( timestamp, fullpath, file )
if timestamp not in list(backups.keys()):
backups[timestamp] = backup(timestamp, fullpath, file)
else:
backups[timestamp].add( file )
backups[timestamp].add(file)

backup_items = sorted(backups.items(), key=lambda t: t[0])
if last: # NOTE: if last == 0, the slice returns an empty list!
backup_items = backup_items[:-last]

for stamp in list(backups.keys()):
for stamp, bck in backup_items:
bck = backups[stamp]
bck_seconds = calendar.timegm( stamp )
bck_seconds = calendar.timegm(stamp)

if bck_seconds > now - (hourly * 3600):
continue

if bck.is_daily() and bck_seconds > now - (daily * 86400):
continue

if bck.is_monthly() and bck_seconds > now - (monthly * 2678400):
continue

if bck_seconds > now - ( hourly * 3600 ):
# print ( "%s is hourly and will be kept" % ( time.asctime( stamp ) ) )
if bck.is_yearly() and bck_seconds > now - (yearly * 31622400):
continue
# else:
# print ("%s is hourly but to old." % ( time.asctime( stamp ) ) )

if bck.is_daily():
if bck_seconds > now - ( daily * 86400 ):
# print( "%s is daily and will be kept." % ( time.asctime( stamp ) ) )
continue
# else:
# print ("%s is daily but to old." % ( time.asctime( stamp ) ) )

if bck.is_monthly():
if bck_seconds > now - ( monthly * 2678400 ):
# print( "%s is monthly and will be kept." % ( time.asctime( stamp ) ) )
continue
# else:
# print ("%s is monthly but to old." % ( time.asctime( stamp ) ) )

if bck.is_yearly():
if bck_seconds > now - ( yearly * 31622400 ):
# print( "%s is yearly and will be kept." % ( time.asctime( stamp ) ) )
continue
# else:
# print ("%s is yearly but to old." % ( time.asctime( stamp ) ) )
# print( "%s will be removed." % ( time.asctime( stamp ) ) )

bck.remove()
8 changes: 8 additions & 0 deletions dbdump/ChangeLog
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# 1.2
* flake8/isort cleanup
* Add new options: ssh-timeout and ssh-options
* Switch to generating sha256 checksums

2013-07-21:
* pep8 cleanup
* add this ChangeLog
2 changes: 1 addition & 1 deletion dbdump/README
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ to get it working.

Simply check out the git-repository:

git clone https://git.fsinf.at/fsinf/db-backup.git
git clone https://github.com/mathiasertl/db-backup.git

If you don't want to specify the full path, you can of course copy dbdump.py
somewhere in your path (/usr/local/bin is usually good). Take care to copy
Expand Down
Loading