check_lv_snap 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. #!/usr/bin/python3
  2. # Copyright (C) 2023 Philipp Fromme
  3. #
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. import argparse
  17. import pytz
  18. import re
  19. import subprocess
  20. import sys
  21. from enum import Enum
  22. from datetime import datetime, timedelta
  23. class State(Enum):
  24. OK = 0
  25. WARNING = 1
  26. CRITICAL = 2
  27. UNKNOWN = 3
  28. def set_state(self, state):
  29. if state.value > self.value:
  30. return state
  31. return self
  32. def main():
  33. parser = argparse.ArgumentParser(description="Check for LVM snapshots overdue for deletion")
  34. parser.add_argument("-w", "--warning", help="Warning threshold, default "
  35. "604800 seconds (one week)", nargs="?", type=int,
  36. default=604800)
  37. parser.add_argument("-c", "--critical", help="Critical threshold, default 1209600 seconds (two weeks)",
  38. nargs="?", type=int, default=1209600)
  39. parser.add_argument("-z", "--tz", help="Set timezone, default Europe/Berlin",
  40. nargs="?", type=str, default='Europe/Berlin')
  41. parser.add_argument("-r", "--regex", help="Only show LVs with names matching regex, "
  42. "matches all snapshots by default", nargs='+', default=[None])
  43. parser.add_argument("-d", "--delete", help="Only show snapshots to be deleted",
  44. action="store_true")
  45. args = parser.parse_args()
  46. if args.critical < args.warning:
  47. sys.exit(f"Critical seconds {args.critical} is less than warning seconds {args.warning}")
  48. cmd = ['/sbin/lvs', '-o', 'lv_name,lv_time', '--separator=|', '--noheadings', '-S', 'lv_attr=~[^s.*]']
  49. snapshots = subprocess.check_output(cmd)
  50. snapshots = snapshots.decode().strip().split('\n')
  51. dt_now = datetime.now(pytz.timezone(args.tz))
  52. state = State.OK
  53. output = ""
  54. for line in snapshots:
  55. if not line:
  56. continue
  57. line = line.strip()
  58. elements = line.split('|')
  59. lv_name = elements[0]
  60. for pattern in args.regex:
  61. if pattern is None:
  62. break
  63. regex = re.compile(pattern)
  64. if regex.match(lv_name):
  65. break
  66. else:
  67. continue
  68. lv_time = elements[1]
  69. dt_lv_time = datetime.strptime(lv_time, '%Y-%m-%d %H:%M:%S %z')
  70. dt_warning = dt_lv_time + timedelta(0,args.warning)
  71. dt_critical = dt_lv_time + timedelta(0,args.critical)
  72. if dt_warning > dt_now and args.delete:
  73. continue
  74. output = (f"{output}\n"
  75. f"{lv_name}:\n"
  76. f" Creation Date: {lv_time}\n"
  77. f" Deletion Date: {dt_warning}")
  78. dt_delta = None
  79. if dt_critical < dt_now:
  80. state = state.set_state(State.CRITICAL)
  81. elif dt_warning < dt_now:
  82. state = state.set_state(State.WARNING)
  83. else:
  84. continue
  85. dt_delta = dt_now - dt_warning
  86. output = (f"{output}\n"
  87. f" Deletion Overdue: {dt_delta}")
  88. if state == State.OK:
  89. output = ("OK: keine zu loeschenden Snapshots gefunden\n"
  90. f"{output}")
  91. elif state == State.WARNING:
  92. output = ("WARNING: folgende Snapshots muessen geloescht werden!\n"
  93. f"{output}")
  94. elif state == State.CRITICAL:
  95. output = ("CRITICAL: einige Snapshots sind bereits DEUTLICH ueber dem Loeschdatum!\n"
  96. f"{output}")
  97. print(output)
  98. sys.exit(state.value)
  99. if __name__ == "__main__":
  100. main()