Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 856fa7eb4c | |||
| d50d3e02ed | |||
| 0016fb8e3b | |||
| 324b19ff09 | |||
| 6a1b6a27cf | |||
| a09317b44a | |||
| 412a576ccd | |||
| bf46208c4d | |||
| ae5a89a21b |
+44
-11
@@ -3,35 +3,68 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
import argparse
|
||||||
|
|
||||||
#_snapshots = open("zfs.txt","r").read()
|
# Argumente verarbeiten
|
||||||
_snapshots = subprocess.check_output("/usr/sbin/zfs list -t snapshot -Hpo name,creation".split())
|
parser = argparse.ArgumentParser(description="ZFS Snapshot Übersicht")
|
||||||
|
parser.add_argument("--filter", help="Nur bestimmte Datasets anzeigen (Regex möglich, z.B. 'rpool/ROOT')", type=str)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Snapshots abrufen
|
||||||
|
_snapshots = subprocess.check_output(["zfs", "list", "-t", "snapshot", "-Hpo", "name,creation"],encoding="utf-8")
|
||||||
|
|
||||||
LABELS=("frequent","hourly","daily","weekly","monthly","yearly","backup-zfs","bashclub-zfs")
|
|
||||||
RE_LABELSEARCH = re.compile("|".join(LABELS))
|
|
||||||
_datasets = {}
|
_datasets = {}
|
||||||
for _datastore,_snapshot,_creation in re.findall("^([\w_./-]+)@([\w_.-]+)\t(\d+)",_snapshots.decode('utf-8'),re.M):
|
|
||||||
|
# Snapshots parsen
|
||||||
|
for _datastore, _snapshot, _creation in re.findall(
|
||||||
|
r"^([\w_./-]+)@([\w_.:-]+)\t(\d+)",
|
||||||
|
_snapshots,
|
||||||
|
re.M):
|
||||||
|
|
||||||
|
if args.filter and not re.search(args.filter, _datastore):
|
||||||
|
continue
|
||||||
|
|
||||||
if _datastore not in _datasets:
|
if _datastore not in _datasets:
|
||||||
_datasets[_datastore] = {}
|
_datasets[_datastore] = {}
|
||||||
_label = RE_LABELSEARCH.search(_snapshot)
|
|
||||||
if _label:
|
# -------------------------------------------------
|
||||||
_label = _label.group(0)
|
# Snapshot-Typ automatisch erkennen
|
||||||
|
# -------------------------------------------------
|
||||||
|
if _snapshot.startswith("zfs-auto-snap_"):
|
||||||
|
m = re.match(r"zfs-auto-snap_([^-\s]+)-", _snapshot)
|
||||||
|
_label = m.group(1) if m else "zfs-auto-snap"
|
||||||
|
|
||||||
|
elif re.match(r"^[^_]+_\d{4}-\d{2}-\d{2}", _snapshot):
|
||||||
|
# Beispiel: bashclub-zfs_2025-09-05_06:25:24
|
||||||
|
_label = _snapshot.split("_")[0]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
_label = "other"
|
# Fallback → alles vor erstem Datum abschneiden
|
||||||
|
_label = re.sub(r'[-_]\d{4}-\d{2}-\d{2}.*$', '', _snapshot)
|
||||||
|
|
||||||
|
# -------------------------------------------------
|
||||||
|
|
||||||
if _label not in _datasets[_datastore]:
|
if _label not in _datasets[_datastore]:
|
||||||
_datasets[_datastore][_label] = []
|
_datasets[_datastore][_label] = []
|
||||||
|
|
||||||
_datasets[_datastore][_label].append((_snapshot, int(_creation)))
|
_datasets[_datastore][_label].append((_snapshot, int(_creation)))
|
||||||
|
|
||||||
for _datastore in _datasets.keys():
|
# Ergebnisse anzeigen
|
||||||
|
for _datastore in _datasets:
|
||||||
print(_datastore)
|
print(_datastore)
|
||||||
print("-" * 40)
|
print("-" * 40)
|
||||||
for _label in _datasets[_datastore].keys():
|
|
||||||
|
for _label in sorted(_datasets[_datastore]):
|
||||||
_data = _datasets[_datastore][_label]
|
_data = _datasets[_datastore][_label]
|
||||||
|
_data.sort(key=lambda x: x[1])
|
||||||
|
|
||||||
_first = time.strftime("%d.%m.%Y %H:%M:%S", time.localtime(_data[0][1]))
|
_first = time.strftime("%d.%m.%Y %H:%M:%S", time.localtime(_data[0][1]))
|
||||||
_last = time.strftime("%d.%m.%Y %H:%M:%S", time.localtime(_data[-1][1]))
|
_last = time.strftime("%d.%m.%Y %H:%M:%S", time.localtime(_data[-1][1]))
|
||||||
_count = len(_data)
|
_count = len(_data)
|
||||||
|
|
||||||
print(f" {_label} {_count}")
|
print(f" {_label} {_count}")
|
||||||
print(f" {_first} {_data[0][0]}")
|
print(f" {_first} {_data[0][0]}")
|
||||||
if _count > 1:
|
if _count > 1:
|
||||||
print(f" {_last} {_data[-1][0]}")
|
print(f" {_last} {_data[-1][0]}")
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
|
|||||||
+25
-15
@@ -16,7 +16,7 @@
|
|||||||
## GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
## GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
## LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
## LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
VERSION = 4.10
|
VERSION = 4.14
|
||||||
|
|
||||||
### for check_mk usage link or copy binary to check_mk_agent/local/checkzfs
|
### for check_mk usage link or copy binary to check_mk_agent/local/checkzfs
|
||||||
### create /etc/check_mk/checkzfs ## the config file name matches the filename in check_mk_agent/local/
|
### create /etc/check_mk/checkzfs ## the config file name matches the filename in check_mk_agent/local/
|
||||||
@@ -227,7 +227,7 @@ class negative_regex_class(object):
|
|||||||
return not self.regex.search(text)
|
return not self.regex.search(text)
|
||||||
|
|
||||||
class zfscheck(object):
|
class zfscheck(object):
|
||||||
ZFSLIST_REGEX = re.compile("^(?P<dataset>.*?)(?:|@(?P<snapshot>.*?))\t(?P<type>\w*)\t(?P<creation>\d+)\t(?P<guid>\d+)\t(?P<used>\d+|-)\t(?P<available>\d+|-)\t(?P<written>\d+|-)\t(?P<origin>.*?)\t(?P<autosnapshot>[-\w]+)\t(?P<checkzfs>[-\w]+)$",re.M)
|
ZFSLIST_REGEX = re.compile(r"^(?P<dataset>.*?)(?:|@(?P<snapshot>.*?))\t(?P<type>\w*)\t(?P<creation>\d+)\t(?P<guid>\d+)\t(?P<used>\d+|-)\t(?P<available>\d+|-)\t(?P<written>\d+|-)\t(?P<origin>.*?)\t(?P<autosnapshot>[-\w]+)\t(?P<checkzfs>[-\w]+)$",re.M)
|
||||||
ZFS_DATASETS = {}
|
ZFS_DATASETS = {}
|
||||||
ZFS_SNAPSHOTS = {}
|
ZFS_SNAPSHOTS = {}
|
||||||
#VALIDCOLUMNS = ["source","replica","type","autosnap","snapshot","creation","guid","used","referenced","size","age","status","message"] ## valid columns
|
#VALIDCOLUMNS = ["source","replica","type","autosnap","snapshot","creation","guid","used","referenced","size","age","status","message"] ## valid columns
|
||||||
@@ -272,7 +272,7 @@ class zfscheck(object):
|
|||||||
}
|
}
|
||||||
COLUMN_MAPPER = {}
|
COLUMN_MAPPER = {}
|
||||||
|
|
||||||
def __init__(self,remote,source,sourceonly,legacyhosts,output,mail=None,prefix='REPLICA',debug=False,**kwargs):
|
def __init__(self,remote,source,sourceonly,legacyhosts,output,ignoreattr,mail=None,prefix='REPLICA',debug=False,**kwargs):
|
||||||
_start_time = time.time()
|
_start_time = time.time()
|
||||||
self.remote_hosts = remote.split(",") if remote else [""] if source and not sourceonly else [] ## wenn nicht und source woanders ... "" (also lokal) als remote
|
self.remote_hosts = remote.split(",") if remote else [""] if source and not sourceonly else [] ## wenn nicht und source woanders ... "" (also lokal) als remote
|
||||||
self.source_hosts = source.split(",") if source else [""] ## wenn nix dann "" als local
|
self.source_hosts = source.split(",") if source else [""] ## wenn nix dann "" als local
|
||||||
@@ -283,6 +283,7 @@ class zfscheck(object):
|
|||||||
self.print_debug(f"Version: {VERSION}")
|
self.print_debug(f"Version: {VERSION}")
|
||||||
self.prefix = prefix.strip().replace(" ","_") ## service name bei checkmk leerzeichen durch _ ersetzen
|
self.prefix = prefix.strip().replace(" ","_") ## service name bei checkmk leerzeichen durch _ ersetzen
|
||||||
self.rawdata = False
|
self.rawdata = False
|
||||||
|
self.ignoreattr = ignoreattr
|
||||||
self.mail_address = mail
|
self.mail_address = mail
|
||||||
self._overall_status = []
|
self._overall_status = []
|
||||||
self.sortreverse = False
|
self.sortreverse = False
|
||||||
@@ -494,7 +495,7 @@ class zfscheck(object):
|
|||||||
yield _match.groupdict()
|
yield _match.groupdict()
|
||||||
|
|
||||||
def _call_proc(self,remote=None):
|
def _call_proc(self,remote=None):
|
||||||
ZFS_ATTRIBUTES = "name,type,creation,guid,used,available,written,origin,com.sun:auto-snapshot,tv.sysops:checkzfs" ## wenn ändern dann auch regex oben anpassen
|
ZFS_ATTRIBUTES = f"name,type,creation,guid,used,available,written,origin,com.sun:auto-snapshot,{self.ignoreattr}" ## wenn ändern dann auch regex oben anpassen
|
||||||
### eigentlicher zfs aufruf, sowohl local als auch remote
|
### eigentlicher zfs aufruf, sowohl local als auch remote
|
||||||
zfs_args = ["zfs", "list",
|
zfs_args = ["zfs", "list",
|
||||||
"-t", "all",
|
"-t", "all",
|
||||||
@@ -611,7 +612,7 @@ class zfscheck(object):
|
|||||||
_written = _item.get("written","0")
|
_written = _item.get("written","0")
|
||||||
_available = _item.get("available","0")
|
_available = _item.get("available","0")
|
||||||
_used = _item.get("used","0")
|
_used = _item.get("used","0")
|
||||||
if _status == -1: ## tv.sysops:checkzfs=ignore wollen wir nicht
|
if _status == -1: ## tv.sysops:checkzfs=ignore wollen wir nicht (ignoreattr)
|
||||||
continue
|
continue
|
||||||
if self.maxsnapshots:
|
if self.maxsnapshots:
|
||||||
_warn = self.maxsnapshots[0]
|
_warn = self.maxsnapshots[0]
|
||||||
@@ -707,7 +708,7 @@ class zfscheck(object):
|
|||||||
if not _email:
|
if not _email:
|
||||||
_users = open("/etc/pve/user.cfg","rt").read()
|
_users = open("/etc/pve/user.cfg","rt").read()
|
||||||
_email = "root@{0}".format(_hostname)
|
_email = "root@{0}".format(_hostname)
|
||||||
_emailmatch = re.search("^user:root@pam:.*?:(?P<mail>[\w.]+@[\w.]+):.*?$",_users,re.M)
|
_emailmatch = re.search(r"^user:root@pam:.*?:(?P<mail>[\w.]+@[\w.]+):.*?$",_users,re.M)
|
||||||
if _emailmatch:
|
if _emailmatch:
|
||||||
_email = _emailmatch.group(1)
|
_email = _emailmatch.group(1)
|
||||||
#raise Exception("No PVE User Email found")
|
#raise Exception("No PVE User Email found")
|
||||||
@@ -768,6 +769,8 @@ if __name__ == "__main__":
|
|||||||
help=_("Nur Snapshot-Alter prüfen"))
|
help=_("Nur Snapshot-Alter prüfen"))
|
||||||
_parser.add_argument("--mail",type=str,
|
_parser.add_argument("--mail",type=str,
|
||||||
help=_("Email für den Versand"))
|
help=_("Email für den Versand"))
|
||||||
|
_parser.add_argument("--ignoreattr",type=str,default="tv.sysops:checkzfs",
|
||||||
|
help=_("ZFS Attribut für ignore"))
|
||||||
_parser.add_argument("--config",dest="config_file",type=str,default="",
|
_parser.add_argument("--config",dest="config_file",type=str,default="",
|
||||||
help=_("Config File"))
|
help=_("Config File"))
|
||||||
_parser.add_argument("--threshold",type=str,
|
_parser.add_argument("--threshold",type=str,
|
||||||
@@ -788,17 +791,18 @@ if __name__ == "__main__":
|
|||||||
help=_("Zuordnung zu anderem Host bei checkmk"))
|
help=_("Zuordnung zu anderem Host bei checkmk"))
|
||||||
_parser.add_argument("--ssh-extra-options",type=str,
|
_parser.add_argument("--ssh-extra-options",type=str,
|
||||||
help=_("zusätzliche SSH Optionen mit Komma getrennt (HostKeyAlgorithms=ssh-rsa)"))
|
help=_("zusätzliche SSH Optionen mit Komma getrennt (HostKeyAlgorithms=ssh-rsa)"))
|
||||||
_parser.add_argument("--update",nargs="?",const="main",type=str,choices=["main","testing"],
|
_parser.add_argument("--update",nargs="?",const="main",type=str,metavar="branch/commitid",
|
||||||
help=_("check for update"))
|
help=_("check for update"))
|
||||||
_parser.add_argument("--debug",action="store_true",
|
_parser.add_argument("--debug",action="store_true",
|
||||||
help=_("debug Ausgabe"))
|
help=_("debug Ausgabe"))
|
||||||
args = _parser.parse_args()
|
args = _parser.parse_args()
|
||||||
|
|
||||||
CONFIG_KEYS="disabled|source|sourceonly|piggyback|remote|legacyhosts|prefix|filter|replicafilter|threshold|maxsnapshots|snapshotfilter|ssh-identity|ssh-extra-options"
|
CONFIG_KEYS="disabled|source|sourceonly|piggyback|remote|legacyhosts|prefix|filter|replicafilter|threshold|ignoreattr|maxsnapshots|snapshotfilter|ssh-identity|ssh-extra-options"
|
||||||
_config_regex = re.compile(f"^({CONFIG_KEYS}):\s*(.*?)(?:\s+#|$)",re.M)
|
_config_regex = re.compile(rf"^({CONFIG_KEYS}):\s*(.*?)(?:\s+#|$)",re.M)
|
||||||
_basename = os.path.basename(__file__).split(".")[0] ## name für config ermitteln aufgrund des script namens
|
_basename = os.path.basename(__file__).split(".")[0] ## name für config ermitteln aufgrund des script namens
|
||||||
_is_checkmk_plugin = os.path.dirname(os.path.abspath(__file__)).find("/check_mk_agent/local") > -1 ## wenn im check_mk ordner
|
#_is_checkmk_plugin = os.path.dirname(os.path.abspath(__file__)).find("/check_mk_agent/local") > -1 ## wenn im check_mk ordner
|
||||||
if _is_checkmk_plugin:
|
#if _is_checkmk_plugin:
|
||||||
|
if os.environ.get("MK_CONFDIR"):
|
||||||
try: ## parse check_mk options
|
try: ## parse check_mk options
|
||||||
_check_mk_configdir = "/etc/check_mk"
|
_check_mk_configdir = "/etc/check_mk"
|
||||||
if not os.path.isdir(_check_mk_configdir):
|
if not os.path.isdir(_check_mk_configdir):
|
||||||
@@ -852,27 +856,32 @@ if __name__ == "__main__":
|
|||||||
import base64
|
import base64
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import difflib
|
import difflib
|
||||||
|
from pkg_resources import parse_version
|
||||||
_github_req = requests.get(f"https://api.github.com/repos/bashclub/check-zfs-replication/contents/checkzfs.py?ref={args.update}")
|
_github_req = requests.get(f"https://api.github.com/repos/bashclub/check-zfs-replication/contents/checkzfs.py?ref={args.update}")
|
||||||
if _github_req.status_code != 200:
|
if _github_req.status_code != 200:
|
||||||
raise Exception("Github Error")
|
raise Exception("Github Error")
|
||||||
_github_version = _github_req.json()
|
_github_version = _github_req.json()
|
||||||
_github_last_modified = datetime.strptime(_github_req.headers.get("last-modified"),"%a, %d %b %Y %X %Z")
|
_github_last_modified = datetime.strptime(_github_req.headers.get("last-modified"),"%a, %d %b %Y %X %Z")
|
||||||
_new_script = base64.b64decode(_github_version.get("content")).decode("utf-8")
|
_new_script = base64.b64decode(_github_version.get("content")).decode("utf-8")
|
||||||
_new_version = re.findall("^VERSION\s*=\s*([0-9.]*)",_new_script,re.M)
|
_new_version = re.findall(r"^VERSION\s*=[\s\x22]*([0-9.]*)",_new_script,re.M)
|
||||||
_new_version = float(_new_version[0]) if _new_version else 0.0
|
_new_version = _new_version[0] if _new_version else "0.0.0"
|
||||||
_script_location = os.path.realpath(__file__)
|
_script_location = os.path.realpath(__file__)
|
||||||
_current_last_modified = datetime.fromtimestamp(int(os.path.getmtime(_script_location)))
|
_current_last_modified = datetime.fromtimestamp(int(os.path.getmtime(_script_location)))
|
||||||
with (open(_script_location,"rb")) as _f:
|
with (open(_script_location,"rb")) as _f:
|
||||||
_content = _f.read()
|
_content = _f.read()
|
||||||
_current_sha = hashlib.sha1(f"blob {len(_content)}\0".encode("utf-8") + _content).hexdigest()
|
_current_sha = hashlib.sha1(f"blob {len(_content)}\0".encode("utf-8") + _content).hexdigest()
|
||||||
_content = _content.decode("utf-8")
|
_content = _content.decode("utf-8")
|
||||||
|
if type(VERSION) != str:
|
||||||
|
VERSION = str(VERSION)
|
||||||
if _current_sha == _github_version.get("sha"):
|
if _current_sha == _github_version.get("sha"):
|
||||||
print(f"allready up to date {_current_sha}")
|
print(f"allready up to date {_current_sha}")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
if VERSION == _new_version:
|
_version = parse_version(VERSION)
|
||||||
|
_nversion = parse_version(_new_version)
|
||||||
|
if _version == _nversion:
|
||||||
print("same Version but checksums mismatch")
|
print("same Version but checksums mismatch")
|
||||||
elif VERSION > _new_version:
|
elif _version > _nversion:
|
||||||
print(f"ATTENTION: Downgrade from {VERSION} to {_new_version}")
|
print(f"ATTENTION: Downgrade from {VERSION} to {_new_version}")
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
@@ -908,3 +917,4 @@ if __name__ == "__main__":
|
|||||||
if args.debug:
|
if args.debug:
|
||||||
raise
|
raise
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user