From d10b04aa80a5fc11fb0859ffc4a30344ebe0d5b9 Mon Sep 17 00:00:00 2001 From: Thorsten Spille Date: Thu, 3 Nov 2022 22:01:44 +0100 Subject: [PATCH] Update 4.08 --- checkzfs.py | 148 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 92 insertions(+), 56 deletions(-) diff --git a/checkzfs.py b/checkzfs.py index c7ac242..9cb1c6c 100644 --- a/checkzfs.py +++ b/checkzfs.py @@ -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 ## 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.02 +VERSION = 4.08 ### 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/ @@ -61,7 +61,6 @@ import json import os.path import os import socket -from datetime import datetime from email.message import EmailMessage from email.mime.application import MIMEApplication from email.utils import formatdate @@ -136,7 +135,7 @@ class zfs_dataset(object): return self.sorted_snapshots()[0] - def get_info(self,source,threshold=None,ignore_replica=False): + def get_info(self,source,threshold=None,maxsnapshots=None,ignore_replica=False): _latest = self._get_latest_snapshot(source if source != self else None) ## wenn das source dataset nicht man selber ist _status = -1 _has_zfs_autosnapshot = any(map(lambda x: str(x.snapshot).startswith("zfs-auto-snap_"),self.snapshots.values())) @@ -178,6 +177,18 @@ class zfs_dataset(object): _message = _("Rollback zu altem Snapshot. - '{0.snapshot}' nicht mehr vorhanden".format(self.latest_snapshot)) _status = 2 ## crit + if maxsnapshots: + _maxsnapshot_status = list( + map(lambda x: x[1], + filter(lambda y: y[0] < len(self.snapshots.keys()), + zip(maxsnapshots,(1,2)) + ) + ) + ) + if _maxsnapshot_status: + if _maxsnapshot_status[-1] > _status: + _message = _("zu viele Snapshots") + _status = _maxsnapshot_status[-1] if not self.checkzfs: _status = -1 @@ -464,7 +475,7 @@ class zfscheck(object): continue #if _dataset.remote in self.remote_hosts:## or _dataset.autosnapshot == 0: ## wenn das dataset von der remote seite ist ... dann weiter oder wenn autosnasphot explizit aus ist ... dann nicht als source hinzufügen # continue - _dataset_info = _dataset.get_info(_dataset,threshold=self.threshold,ignore_replica=self.sourceonly) + _dataset_info = _dataset.get_info(_dataset,threshold=self.threshold,maxsnapshots=self.maxsnapshots,ignore_replica=self.sourceonly) self._overall_status.append(_dataset_info.get("status",-1)) ## alle stati für email overall status _output.append(_dataset_info) if self.sourceonly == True: @@ -472,7 +483,7 @@ class zfscheck(object): for _replica in _dataset.replica: ## jetzt das dataset welches als source angezeigt wird (alle filter etc entsprochen nach replika durchsuchen #if not self.replicafilter.search(_replica.dataset_name): # continue - _replica_info = _replica.get_info(_dataset,threshold=self.threshold) ## verarbeitung ausgabe aus klasse + _replica_info = _replica.get_info(_dataset,threshold=self.threshold,maxsnapshots=self.maxsnapshots) ## verarbeitung ausgabe aus klasse self._overall_status.append(_replica_info.get("status",-1)) ## fehler aus replica zu overall status für mail adden _output.append(_replica_info) @@ -531,8 +542,11 @@ class zfscheck(object): raise Exception(_stderr.decode(sys.stdout.encoding)) ## Raise Errorlevel with Error from proc -- kann check_mk stderr lesen? sollte das nach stdout? return _stdout.decode(sys.stdout.encoding) ## ausgabe kommt als byte wir wollen str - def convert_ts_date(self,ts): - return time.strftime(self.DATEFORMAT,time.localtime(ts)) + def convert_ts_date(self,ts,dateformat=None): + if dateformat: + return time.strftime(dateformat,time.localtime(ts)) + else: + return time.strftime(self.DATEFORMAT,time.localtime(ts)) @staticmethod def format_status(val): @@ -602,15 +616,15 @@ class zfscheck(object): if self.maxsnapshots: _warn = self.maxsnapshots[0] _crit = self.maxsnapshots[1] - _maxsnapshots = f"{_warn};{_crit}" - if _status == 0: - _status = "P" + _maxsnapshots = f"{_warn};{_crit}".replace("inf","") + #if _status == 0: + # _status = "P" else: _maxsnapshots = ";" if self.threshold: _warn = self.threshold[0] * 60 _crit = self.threshold[1] * 60 - _threshold = f"{_warn};{_crit}" + _threshold = f"{_warn};{_crit}".replace("inf","") else: _threshold = ";" _msg = _item.get("message","").strip() @@ -661,31 +675,30 @@ class zfscheck(object): _header_names = [self.COLUMN_NAMES.get(i,i) for i in _header] _converter = dict((i,self.COLUMN_MAPPER.get(i,(lambda x: str(x)))) for i in _header) _hostname = socket.getfqdn() - - _out = "" - _out += "" - _out += "" - _out += "" - _out += "Check ZFS" - _out += f"

{_hostname}

{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}

" - _out += "" - _out += "".format("".format("
{0}
".join(_header_names)) - + _now = self.convert_ts_date(time.time(),'%Y-%m-%d %H:%M:%S') + _out = [] + _out.append("") + _out.append("") + _out.append("") + _out.append("") + _out.append("Check ZFS") + _out.append(f"

{_hostname}

{_now}

") + _out.append("") + _out.append("".format("".format("
{0}
".join(_header_names))) for _item in self._datasort(data): - _out += "
{0}
".join([_converter.get(_col)(_item.get(_col,"")) for _col in _header]),_converter["status"](_item.get("status","0"))) - - _out += "
" + _out.append("
{0}
".join([_converter.get(_col)(_item.get(_col,"")) for _col in _header]),_converter["status"](_item.get("status","0")))) + _out.append("
") return "".join(_out) def mail_output(self,data): @@ -755,6 +768,8 @@ if __name__ == "__main__": help=_("Nur Snapshot-Alter prüfen")) _parser.add_argument("--mail",type=str, help=_("Email für den Versand")) + _parser.add_argument("--config",dest="config_file",type=str,default="", + help=_("Config File")) _parser.add_argument("--threshold",type=str, help=_("Grenzwerte für Alter von Snapshots warn,crit")) _parser.add_argument("--maxsnapshots",type=str, @@ -776,36 +791,58 @@ if __name__ == "__main__": _parser.add_argument("--debug",action="store_true", help=_("debug Ausgabe")) args = _parser.parse_args() + + CONFIG_KEYS="disabled|source|sourceonly|piggyback|remote|legacyhosts|prefix|filter|replicafilter|threshold|maxsnapshots|snapshotfilter|ssh-identity|ssh-extra-options" + _config_regex = re.compile(f"^({CONFIG_KEYS}):\s*(.*?)(?:\s+#|$)",re.M) + _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 if _is_checkmk_plugin: try: ## parse check_mk options - CONFIG_KEYS="disabled|source|sourceonly|piggyback|remote|legacyhosts|prefix|filter|replicafilter|threshold|maxsnapshots|snapshotfilter|ssh-identity|ssh-extra-options" - _config_regex = re.compile(f"^({CONFIG_KEYS}):\s*(.*?)(?:\s+#|$)",re.M) - _basename = os.path.basename(__file__).split(".")[0] ## name für config ermitteln aufgrund des script namens - _config_file = f"/etc/check_mk/{_basename}" - if not os.path.exists(_config_file): ### wenn checkmk aufruf und noch keine config ... default erstellen - if not os.path.isdir("/etc/check_mk"): - os.mkdir("/etc/check_mk") - with open(_config_file,"wt") as _f: ## default config erstellen + _check_mk_configdir = "/etc/check_mk" + if not os.path.isdir(_check_mk_configdir): + _check_mk_configdir = "/etc/check_mk" + args.config_file = f"{_check_mk_configdir}/{_basename}" + if not os.path.exists(args.config_file): ### wenn checkmk aufruf und noch keine config ... default erstellen + if not os.path.isdir(_check_mk_configdir): + os.mkdir(_check_mk_configdir) + with open(args.config_file,"wt") as _f: ## default config erstellen _f.write("## config for checkzfs check_mk") _f.write("\n".join([f"# {_k}:" for _k in CONFIG_KEYS.split("|")])) _f.write("\n") - print(f"please edit config {_config_file}") + print(f"please edit config {args.config_file}") os._exit(0) - _rawconfig = open(_config_file,"rt").read() - for _k,_v in _config_regex.findall(_rawconfig): - if _k == "disabled" and _v.lower().strip() in ( "1","yes","true"): ## wenn disabled dann ignorieren check wird nicht durchgeführt - os._exit(0) - if _k == "sourceonly": - args.sourceonly = bool(_v.lower().strip() in ( "1","yes","true")) - elif _k == "prefix": - args.__dict__["prefix"] = _v.strip() - elif not args.__dict__.get(_k.replace("-","_"),None): - args.__dict__[_k.replace("-","_")] = _v.strip() - except: pass args.output = "checkmk" if not args.output else args.output + _is_zabbix_plugin = os.path.dirname(os.path.abspath(__file__)).find("/zabbix/scripts") > -1 ## wenn im check_mk ordner + if _is_zabbix_plugin: + try: ## parse check_mk options + args.config_file = f"/etc/zabbix/checkzfs-{_basename}" + if not os.path.exists(args.config_file): ### wenn checkmk aufruf und noch keine config ... default erstellen + if not os.path.isdir("/etc/zabbix"): + os.mkdir("/etc/zabbix") + with open(args.config_file,"wt") as _f: ## default config erstellen + _f.write("## config for checkzfs zabbix") + _f.write("\n".join([f"# {_k}:" for _k in CONFIG_KEYS.split("|")])) + _f.write("\n") + print(f"please edit config {args.config_file}") + os._exit(0) + except: + pass + args.output = "json" if not args.output else args.output + + if args.config_file: + _rawconfig = open(args.config_file,"rt").read() + for _k,_v in _config_regex.findall(_rawconfig): + if _k == "disabled" and _v.lower().strip() in ( "1","yes","true"): ## wenn disabled dann ignorieren check wird nicht durchgeführt + os._exit(0) + if _k == "sourceonly": + args.sourceonly = bool(_v.lower().strip() in ( "1","yes","true")) + elif _k == "prefix": + args.__dict__["prefix"] = _v.strip() + elif not args.__dict__.get(_k.replace("-","_"),None): + args.__dict__[_k.replace("-","_")] = _v.strip() + try: ZFSCHECK_OBJ = zfscheck(**args.__dict__) pass ## for debugger @@ -817,4 +854,3 @@ if __name__ == "__main__": if args.debug: raise sys.exit(1) -