Compare commits

..

6 Commits

Author SHA1 Message Date
server
161f018cb9 Bugfix attempt: When a file disappears before its ctime can be checked, don't let the whole program crash 2024-09-29 20:21:46 -07:00
mike
565c1d31b9 copyright year 2024-07-25 17:00:58 -07:00
mike
1517f64424 Setting up more config defaults 2024-07-25 16:44:25 -07:00
mike
cf3bd8eb85 Default to muting initial test logs 2024-07-25 16:18:26 -07:00
mike
2ef7aaf195 Allow "None" for maximum age in config 2024-07-25 16:14:29 -07:00
mike
3c4ed5f792 Add option to mute test logs 2024-06-22 15:31:31 -07:00
6 changed files with 59 additions and 17 deletions

View File

@ -7,7 +7,7 @@ Suppose you have a third party backup program regularly dropping backup files in
# License # License
Copyright 2023 Mike Peralta; All rights reserved Copyright 2024 Mike Peralta; All rights reserved
Releasing to the public under the GNU GENERAL PUBLIC LICENSE v3 (See LICENSE file for more) Releasing to the public under the GNU GENERAL PUBLIC LICENSE v3 (See LICENSE file for more)

View File

@ -31,13 +31,17 @@ class BackupRotator:
config_paths: [Path] = None, config_paths: [Path] = None,
debug: bool = False, debug: bool = False,
systemd: bool = False, systemd: bool = False,
write_to_syslog: bool = False write_to_syslog: bool = False,
do_test_logs: bool = True,
): ):
self.__do_test_logs = do_test_logs
self.__logger = Logger( self.__logger = Logger(
name=type(self).__name__, name=type(self).__name__,
debug=debug, debug=debug,
systemd=systemd, systemd=systemd,
write_to_syslog=write_to_syslog, write_to_syslog=write_to_syslog,
do_test_logs=do_test_logs,
) )
self.__config = Config( self.__config = Config(
@ -322,7 +326,12 @@ class BackupRotator:
best_ctime = None best_ctime = None
for item in items: for item in items:
ctime = Util.detect_item_creation_date(config, item) try:
ctime = Util.detect_item_creation_date(config, item)
except FileNotFoundError as e:
self.__logger.error(f"File disappeared while trying to check ctime: {item}")
continue
if best_ctime is None or ctime < best_ctime: if best_ctime is None or ctime < best_ctime:
best_ctime = ctime best_ctime = ctime
best_item = item best_item = item

View File

@ -12,13 +12,15 @@ class Logger:
name: str, name: str,
debug: bool = False, debug: bool = False,
write_to_syslog: bool = False, write_to_syslog: bool = False,
systemd: bool = False systemd: bool = False,
do_test_logs: bool = True,
): ):
self.__name = name self.__name = name
self.__debug = debug self.__debug = debug
self.__write_to_syslog = write_to_syslog self.__write_to_syslog = write_to_syslog
self.__systemd = systemd self.__systemd = systemd
self.__do_test_logs = do_test_logs
self._init_logger() self._init_logger()
@ -73,10 +75,11 @@ class Logger:
self.__logger.addHandler(handler) self.__logger.addHandler(handler)
# This is annoying inside cron # This is annoying inside cron
self.debug("Test debug log") if self.__do_test_logs:
self.info("Test info log") self.debug("Test debug log")
self.warn("Test warn log") self.info("Test info log")
self.error("Test error log") self.warn("Test warn log")
self.error("Test error log")
def debug(self, s): def debug(self, s):
self.__logger.debug(s) self.__logger.debug(s)

View File

@ -42,6 +42,8 @@ class Util:
# print("got mtime") # print("got mtime")
stat = item.stat().st_birthtime stat = item.stat().st_birthtime
# print("got btime") # print("got btime")
except FileNotFoundError as e:
raise e
except AttributeError: except AttributeError:
pass pass

View File

@ -18,6 +18,10 @@ class ConfigFile:
"file" "file"
] ]
__DEFAULT_MINIMUM_ITEMS = 0
__DEFAULT_MAXIMUM_ITEMS = None
__DEFAULT_MAXIMUM_AGE = None
def __init__( def __init__(
self, logger: Logger, self, logger: Logger,
path: Path, path: Path,
@ -39,9 +43,9 @@ class ConfigFile:
self.__rotatable_paths: [Path] = [] self.__rotatable_paths: [Path] = []
self.__minimum_items: int = 0 self.__minimum_items = self.__DEFAULT_MINIMUM_ITEMS
# noinspection PyTypeChecker # noinspection PyTypeChecker
self.__maximum_items: int = None self.__maximum_items: int = self.__DEFAULT_MAXIMUM_ITEMS
# noinspection PyTypeChecker # noinspection PyTypeChecker
self.__maximum_age: int = None self.__maximum_age: int = None
@ -119,8 +123,11 @@ class ConfigFile:
minimum_items = options["minimum-items"] minimum_items = options["minimum-items"]
self.info(f"Found minimum-items option: {minimum_items}") self.info(f"Found minimum-items option: {minimum_items}")
if minimum_items is None:
minimum_items = self.__DEFAULT_MINIMUM_ITEMS
assert isinstance(minimum_items, int), ( assert isinstance(minimum_items, int), (
f"Option minimum-items must be int, but got: {minimum_items}" f"Option minimum-items must be an integer,"
f" but got: {type(minimum_items).__name__} ({minimum_items})"
) )
self.__minimum_items = minimum_items self.__minimum_items = minimum_items
else: else:
@ -140,10 +147,10 @@ class ConfigFile:
maximum_items = options["maximum-items"] maximum_items = options["maximum-items"]
self.info(f"Found maximum-items option: {maximum_items}") self.info(f"Found maximum-items option: {maximum_items}")
assert isinstance(maximum_items, int), ( assert maximum_items is None or isinstance(maximum_items, int), (
f"Option maximum-items must be int, but got: {maximum_items}" f"Option maximum-items must be integer, but got: {maximum_items}"
) )
assert maximum_items > 0, ( assert maximum_items is None or maximum_items > 0, (
f"Option maximum-items is zero, which doesn't make sense." f"Option maximum-items is zero, which doesn't make sense."
) )
self.__maximum_items = maximum_items self.__maximum_items = maximum_items
@ -156,10 +163,11 @@ class ConfigFile:
maximum_age = options["maximum-age"] maximum_age = options["maximum-age"]
self.info(f"Found maximum-age option (max age in days): {maximum_age}") self.info(f"Found maximum-age option (max age in days): {maximum_age}")
assert isinstance(maximum_age, int), ( assert maximum_age is None or isinstance(maximum_age, int), (
f"Option maximum-age must be int, but got: {maximum_age}" f"Option maximum-age must be None or an integer,"
f" but got: {type(maximum_age).__name__} ({maximum_age})"
) )
assert maximum_age > 0, ( assert maximum_age is None or maximum_age > 0, (
f"Option maximum-age is zero, which doesn't make sense." f"Option maximum-age is zero, which doesn't make sense."
) )
self.__maximum_age = maximum_age self.__maximum_age = maximum_age

20
main.py
View File

@ -42,6 +42,25 @@ def main():
) )
) )
parser.add_argument(
"--no-test-logs",
default=False,
dest="do_test_logs",
action="store_false",
help=(
"Pass if you do not want to see test logs for all log levels."
)
)
parser.add_argument(
"--test-logs",
default=True,
dest="do_test_logs",
action="store_true",
help=(
"Pass if you want to see test logs for all log levels."
)
)
parser.add_argument( parser.add_argument(
"--config", "-c", "--config", "-c",
dest="config_paths", dest="config_paths",
@ -68,6 +87,7 @@ def main():
debug=args.debug, debug=args.debug,
systemd=args.systemd, systemd=args.systemd,
write_to_syslog=args.write_to_syslog, write_to_syslog=args.write_to_syslog,
do_test_logs=args.do_test_logs,
) )
rotator.run( rotator.run(
global_dry_run=args.global_dry_run global_dry_run=args.global_dry_run