From cf4fc02e28e6afdc21c3d37d107b36360a84b905 Mon Sep 17 00:00:00 2001 From: thl-cmk <thl-cmk@outlook.com> Date: Mon, 1 Mar 2021 20:31:33 +0100 Subject: [PATCH] update project --- agent_based/checkpoint_asg_diag.py | 37 ++++++------ checkpoint_asg_diag.mkp | Bin 2996 -> 3154 bytes web/plugins/wato/checkpoint_asg_diag.py | 75 +++++++++++++++--------- 3 files changed, 64 insertions(+), 48 deletions(-) diff --git a/agent_based/checkpoint_asg_diag.py b/agent_based/checkpoint_asg_diag.py index 935b6c7..e4528a0 100644 --- a/agent_based/checkpoint_asg_diag.py +++ b/agent_based/checkpoint_asg_diag.py @@ -97,14 +97,6 @@ class CheckPointASGDiag(NamedTuple): tests: List -class CheckPointASGDiagTest(NamedTuple): - index: str - name: str - lastrun: str - result: str - comment: str - - def parse_checkpoint_asg_diag(string_table: List[StringTable]) -> CheckPointASGDiag: asgSummary, asgTests = string_table @@ -123,10 +115,15 @@ def discovery_checkpoint_asg_diag(section: CheckPointASGDiag) -> DiscoveryResult def check_checkpoint_asg_diag(params, section: CheckPointASGDiag): - print(section.summary) - pprint(section.tests) - - summary = section.summary + class CheckPointASGDiagTest(NamedTuple): + index: str + name: str + lastrun: str + result: str + comment: str + + summary = 'Last run ' + section.summary + print (summary) details = '\n\nTo verify this output use the "asg diag verify" command on the Check Point SMO cli,\n' state = State.OK @@ -140,19 +137,21 @@ def check_checkpoint_asg_diag(params, section: CheckPointASGDiag): if len(asg_diag_ignore) > 0: summary += ', Ignored tests: %s' % str(asg_diag_ignore).strip('[').strip(']').replace(' ', '') - for asgTestIndex, asgTestName, asgTestLastRun, asgTestResult, asgTestComment in section.tests: - if asgTestComment == '': + for test in section.tests: + test = CheckPointASGDiagTest(*test) + if test.comment == '': details += '\nIndex: %s, Name: %s, LastRun: %s, Restult: %s' % ( - asgTestIndex, asgTestName, asgTestLastRun, asgTestResult) + test.index, test.name, test.lastrun, test.result) else: details += '\nIndex: %s, Name: %s, LastRun: %s, Restult: %s, Comment: %s' % ( - asgTestIndex, asgTestName, asgTestLastRun, asgTestResult, asgTestComment) + test.index, test.name, test.result, test.result, test.comment) - asgTestIndex = int(asgTestIndex) + asgTestIndex = int(test.index) if not asgTestIndex in asg_diag_ignore: - if asgTestResult.lower() == 'failed (!)': + if test.result.lower() == 'failed (!)': if asgTestIndex in asg_diag_warning: - state = max(state, State.WARNING) + if state == State.OK: + state = State.WARN else: state = State.CRIT diff --git a/checkpoint_asg_diag.mkp b/checkpoint_asg_diag.mkp index 86f4afa62986846580f39342762ac47697d836cc..53e116f3346f6001c4b6748fd4110d040e77dcb4 100644 GIT binary patch delta 3144 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 delta 2985 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 diff --git a/web/plugins/wato/checkpoint_asg_diag.py b/web/plugins/wato/checkpoint_asg_diag.py index ce201fa..b65f88b 100644 --- a/web/plugins/wato/checkpoint_asg_diag.py +++ b/web/plugins/wato/checkpoint_asg_diag.py @@ -2,33 +2,50 @@ # -*- coding: utf-8 -*- # # -register_check_parameters( - subgroup_applications, - 'checkpoint_asg_diag', - _('Check Point ASG Diag'), - Dictionary( - # help=_(''), - elements=[ - ('asg_diag_ignore', - ListOfStrings( - title=_('Index list of ASG tests to ignore'), - orientation='horizontal', - allow_empty=False, - valuespec=Integer(minvalue=1, maxvalue=99, allow_empty=False), - help=_('there will be no warning/critical if this tests are not "Passed"'), - ) - ), - ('asg_diag_warning', - ListOfStrings( - title=_('Set monitoring state to warining if ASG test state is "Failed"'), - orientation='horizontal', - allow_empty=False, - valuespec=Integer(minvalue=1, maxvalue=99, allow_empty=False), - help=_('there will be only warning if this tests are not "Passed", else there will be a critical.'), - ) - ), - ], - ), - None, - match_type='dict', +from cmk.gui.i18n import _ +from cmk.gui.valuespec import ( + Dictionary, + Integer, + TextAscii, + ListOfStrings, ) + +from cmk.gui.plugins.wato import ( + CheckParameterRulespecWithItem, + rulespec_registry, + RulespecGroupCheckParametersNetworking, +) + + +def _parameter_valuespec_checkpoint_asg_diag(): + return Dictionary(elements=[ + ('asg_diag_ignore', + ListOfStrings( + title=_('Index list of ASG Diag tests to ignore'), + orientation='horizontal', + allow_empty=False, + valuespec=Integer(minvalue=1, maxvalue=99), + help=_('there will be no warning/critical if this tests are not "Passed"'), + ) + ), + ('asg_diag_warning', + ListOfStrings( + title=_('Set monitoring state to warining if ASG Diag state is "Failed" for the following tests'), + orientation='horizontal', + allow_empty=False, + valuespec=Integer(minvalue=1, maxvalue=99), + help=_('there will be only warning if this tests are not "Passed", else there will be a critical.'), + ) + ), + ]) + + +rulespec_registry.register( + CheckParameterRulespecWithItem( + check_group_name='checkpoint_asg_diag', + group=RulespecGroupCheckParametersNetworking, + item_spec=lambda: TextAscii(title=_('Check Point ASG Diag'), ), + match_type='dict', + parameter_valuespec=_parameter_valuespec_checkpoint_asg_diag, + title=lambda: _('Check Point ASG Diag'), + )) -- GitLab