initial commit
This commit is contained in:
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Matthias Blankertz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
python-dateutil
|
||||||
19
setup.py
Normal file
19
setup.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
with open('LICENSE') as f:
|
||||||
|
license = f.read()
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='zfs_smart_snapshot',
|
||||||
|
version='0.0.0',
|
||||||
|
description='Smart snapshoting for ZFS',
|
||||||
|
author='Matthias Blankertz',
|
||||||
|
author_email='matthias@blankertz.org',
|
||||||
|
license=license,
|
||||||
|
packages=find_packages(exclude=('tests', 'docs')),
|
||||||
|
entry_points={
|
||||||
|
'console_scripts': [
|
||||||
|
'zfs_smart_snapshot = zfs_smart_snapshot.main:main',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
5
tests/context.py
Normal file
5
tests/context.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||||
|
|
||||||
|
import zfs_smart_snapshot
|
||||||
39
tests/test_keep.py
Normal file
39
tests/test_keep.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from zfs_smart_snapshot.main import keep_year, keep_month, keep_day, keep_hour, keep_frequent
|
||||||
|
|
||||||
|
|
||||||
|
dates = [datetime(2018, 2, 3), datetime(2019, 3, 4), datetime(2020, 4, 5), datetime(2021, 1, 2),
|
||||||
|
datetime(2021, 2, 2), datetime(2021, 2, 3), datetime(2021, 3, 3), datetime(2021, 3, 4),
|
||||||
|
datetime(2021, 3, 5)]
|
||||||
|
now = datetime(2021, 3, 5, 22, 45, 0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_keep_year():
|
||||||
|
assert keep_year(now, dates, 2) == {datetime(2020, 4, 5), datetime(2021, 1, 2)}
|
||||||
|
assert keep_year(now, dates, 0) == set()
|
||||||
|
dates_gap = [datetime(2018, 2, 3), datetime(2019, 3, 4), datetime(2021, 5, 6)]
|
||||||
|
assert keep_year(datetime(2021, 5, 6), dates_gap, 3) == {datetime(2019, 3, 4), datetime(2021, 5, 6)}
|
||||||
|
assert keep_year(datetime(2022, 2, 3), dates_gap, 1) == set()
|
||||||
|
|
||||||
|
|
||||||
|
def test_keep_month():
|
||||||
|
assert keep_month(now, dates, 3) == {datetime(2021, 1, 2), datetime(2021, 2, 2), datetime(2021, 3, 3)}
|
||||||
|
|
||||||
|
|
||||||
|
def test_keep_day():
|
||||||
|
assert keep_day(now, dates, 3) == {datetime(2021, 3, 3), datetime(2021, 3, 4), datetime(2021, 3, 5)}
|
||||||
|
|
||||||
|
|
||||||
|
datetimes = [datetime(2021, 3, 5, 17, 0, 0), datetime(2021, 3, 5, 18, 0, 0), datetime(2021, 3, 5, 19, 0, 0),
|
||||||
|
datetime(2021, 3, 5, 20, 0, 0), datetime(2021, 3, 5, 21, 0, 0), datetime(2021, 3, 5, 22, 0, 0),
|
||||||
|
datetime(2021, 3, 5, 22, 15, 0), datetime(2021, 3, 5, 22, 30, 0), datetime(2021, 3, 5, 22, 45, 0)]
|
||||||
|
|
||||||
|
|
||||||
|
def test_keep_hour():
|
||||||
|
assert keep_hour(now, datetimes, 3) == {datetime(2021, 3, 5, 20, 0, 0), datetime(2021, 3, 5, 21, 0, 0),
|
||||||
|
datetime(2021, 3, 5, 22, 0, 0)}
|
||||||
|
|
||||||
|
|
||||||
|
def test_keep_frequent():
|
||||||
|
assert keep_frequent(now, datetimes, 3) == {datetime(2021, 3, 5, 22, 15, 0), datetime(2021, 3, 5, 22, 30, 0),
|
||||||
|
datetime(2021, 3, 5, 22, 45, 0)}
|
||||||
1
zfs_smart_snapshot/__init__.py
Normal file
1
zfs_smart_snapshot/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
65
zfs_smart_snapshot/main.py
Normal file
65
zfs_smart_snapshot/main.py
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
|
|
||||||
|
def keep_date(now, snapshots, date_convert, delta, count):
|
||||||
|
keep = set()
|
||||||
|
if count < 1:
|
||||||
|
return keep
|
||||||
|
start_date = date_convert(now) - delta*(count-1)
|
||||||
|
for snapshot in snapshots:
|
||||||
|
if snapshot.date() >= start_date:
|
||||||
|
keep.add(snapshot)
|
||||||
|
start_date = date_convert(snapshot) + delta
|
||||||
|
if start_date > now.date():
|
||||||
|
break
|
||||||
|
return keep
|
||||||
|
|
||||||
|
|
||||||
|
def keep_year(now, snapshots, count):
|
||||||
|
return keep_date(now, snapshots, lambda d: d.date().replace(day=1, month=1), relativedelta(years=1), count)
|
||||||
|
|
||||||
|
|
||||||
|
def keep_month(now, snapshots, count):
|
||||||
|
return keep_date(now, snapshots, lambda d: d.date().replace(day=1), relativedelta(months=1), count)
|
||||||
|
|
||||||
|
|
||||||
|
def keep_day(now, snapshots, count):
|
||||||
|
return keep_date(now, snapshots, lambda d: d.date(), relativedelta(days=1), count)
|
||||||
|
|
||||||
|
|
||||||
|
def keep_time(now, snapshots, convert, delta, count):
|
||||||
|
keep = set()
|
||||||
|
if count < 1:
|
||||||
|
return keep
|
||||||
|
start = convert(now) - delta*(count-1)
|
||||||
|
for snapshot in snapshots:
|
||||||
|
if snapshot >= start:
|
||||||
|
keep.add(snapshot)
|
||||||
|
start = convert(snapshot) + delta
|
||||||
|
if start > now:
|
||||||
|
break
|
||||||
|
return keep
|
||||||
|
|
||||||
|
|
||||||
|
def keep_hour(now, snapshots, count):
|
||||||
|
return keep_time(now, snapshots, lambda d: d.replace(minute=0, second=0, microsecond=0), relativedelta(hours=1),
|
||||||
|
count)
|
||||||
|
|
||||||
|
|
||||||
|
def keep_frequent(now, snapshots, count):
|
||||||
|
return keep_time(now, snapshots, lambda d: d.replace(minute=d.minute//15*15, second=0, microsecond=0),
|
||||||
|
relativedelta(minutes=15), count)
|
||||||
|
|
||||||
|
|
||||||
|
def snapshots_to_keep(now, snapshots, yearly=0, monthly=0, weekly=0, daily=0, hourly=0, frequently=0):
|
||||||
|
snapshots.sort()
|
||||||
|
keep = set()
|
||||||
|
keep.union(keep_year(now, snapshots, yearly))
|
||||||
|
keep.union(keep_month(now, snapshots, monthly))
|
||||||
|
keep.union(keep_day(now, snapshots, daily))
|
||||||
|
return keep
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("Hello, world!")
|
||||||
Reference in New Issue
Block a user