From 75dc9c995bf2c0a720af5cd9c3e918c2e3e28039 Mon Sep 17 00:00:00 2001
From: "Th.L" <thl-cmk@outlook.com>
Date: Thu, 25 Mar 2021 14:50:19 +0100
Subject: [PATCH] update project

---
 agent_based/cisco_asyncos_bandwidth.py   |  22 +-
 agent_based/cisco_asyncos_license.py     |  10 +-
 agent_based/cisco_asyncos_update.py      | 312 +++++++++++++++++++++++
 checks/cisco_asyncos_update              | 259 -------------------
 cisco_asyncos.mkp                        | Bin 11974 -> 11591 bytes
 packages/cisco_asyncos                   |   4 +-
 web/plugins/wato/cisco_asyncos_queue.py  |  58 +++--
 web/plugins/wato/cisco_asyncos_update.py |  74 ++++--
 8 files changed, 424 insertions(+), 315 deletions(-)
 create mode 100644 agent_based/cisco_asyncos_update.py
 delete mode 100644 checks/cisco_asyncos_update

diff --git a/agent_based/cisco_asyncos_bandwidth.py b/agent_based/cisco_asyncos_bandwidth.py
index 0b6374c..6fdcc67 100644
--- a/agent_based/cisco_asyncos_bandwidth.py
+++ b/agent_based/cisco_asyncos_bandwidth.py
@@ -51,16 +51,32 @@ def discovery_cisco_asyncos_bandwidth(section: CiscoAsyncosBandwidth) -> Discove
 
 def check_cisco_asyncos_bandwidth(params, section: CiscoAsyncosBandwidth) -> CheckResult:
     yield from check_levels(
-        section.bandwidth_now,
+        section.bandwidth_now * 8,
         label='Current bandwidth',
         levels_upper=params.get('upper', None),
         levels_lower=params.get('lower', None),
         metric_name='bandwith_now',
+        render_func=render.networkbandwidth,
+        unit='bits/s'
+    )
+    yield from check_levels(
+        section.bandwidth_hour * 8,
+        label='last hour',
+        # levels_upper=params.get('upper', None),
+        # levels_lower=params.get('lower', None),
+        # metric_name='bandwith_now',
+        render_func=render.networkbandwidth
+    )
+    yield from check_levels(
+        section.bandwidth_day * 8,
+        label='last day',
+        #levels_upper=params.get('upper', None),
+        #levels_lower=params.get('lower', None),
+        #metric_name='bandwith_now',
         render_func=render.networkbandwidth
     )
 
-    yield Result(state=State.OK,
-                 summary='last hour: %s, last day: %s' % (section.bandwidth_hour, section.bandwidth_day))
+    # yield Result(state=State.OK, summary='last hour: %s, last day: %s' % (section.bandwidth_hour, section.bandwidth_day))
 
 
 register.snmp_section(
diff --git a/agent_based/cisco_asyncos_license.py b/agent_based/cisco_asyncos_license.py
index 3d8bc5a..91f8e42 100644
--- a/agent_based/cisco_asyncos_license.py
+++ b/agent_based/cisco_asyncos_license.py
@@ -185,6 +185,7 @@ def parse_cisco_asyncos_license(string_table: List[StringTable]) -> Mapping[str,
 def discovery_cisco_asyncos_license(section: Mapping[str, CiscoAsyncosLicense]) -> DiscoveryResult:
     yield Service()
 
+
 # Parameters({'expire': (400, 360), 'features_ignore': ['Data Loss Prevention']})
 def check_cisco_asyncos_license(params, section: Mapping[str, CiscoAsyncosLicense]) -> CheckResult:
     features_ignore = params.get('features_ignore', [])
@@ -194,10 +195,11 @@ def check_cisco_asyncos_license(params, section: Mapping[str, CiscoAsyncosLicens
         if section[license].perpetual:
             yield Result(state=State.OK, notice='%s is perpetual (will not expire)' % license)
         elif license in features_ignore:
-            yield Result(state=State.OK, notice='%s will expire at %s (%s days). Feature ignored!' % (license, section[license].expiredate, section[license].daysuntilexpire))
+            yield Result(state=State.OK, notice='%s will expire at %s (%s days). Feature ignored!' % (
+            license, section[license].expiredate, section[license].daysuntilexpire))
             ignore_count += 1
         elif section[license].secondsuntilexpire == 0:
-            yield  Result(state=State.CRIT, notice='%s has expired or is not licensed' % license)
+            yield Result(state=State.CRIT, notice='%s has expired or is not licensed' % license)
         else:
             if section[license].daysuntilexpire < crit:
                 state = State.CRIT
@@ -206,10 +208,12 @@ def check_cisco_asyncos_license(params, section: Mapping[str, CiscoAsyncosLicens
             else:
                 state = State.OK
 
-            yield Result(state=state, notice='%s will expire at %s (%s days)' % (license, section[license].expiredate, section[license].daysuntilexpire))
+            yield Result(state=state, notice='%s will expire at %s (%s days)' % (
+            license, section[license].expiredate, section[license].daysuntilexpire))
 
     yield Result(state=State.OK, summary='%d/%d Features found/ignored' % (len(section.keys()), ignore_count))
 
+
 register.snmp_section(
     name='cisco_asyncos_license',
     parse_function=parse_cisco_asyncos_license,
diff --git a/agent_based/cisco_asyncos_update.py b/agent_based/cisco_asyncos_update.py
new file mode 100644
index 0000000..3e18099
--- /dev/null
+++ b/agent_based/cisco_asyncos_update.py
@@ -0,0 +1,312 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# License: GNU General Public License v2
+#
+# Author: thl-cmk[at]outlook[dot]com
+# URL   : https://thl-cmk.hopto.org
+# Date  : 2020-02-19
+#
+# 2020-05-14: added wato oprion to ignore items
+# 2021-03-25: rewrite for CMK2.0
+#
+#
+# sample snmpwalk
+#
+# OMD[cmk16x]:~$ snmpwalk -v2c -c public -m ASYNCOS-MAIL-MIB -ObentU localhost updateEntry
+# .1.3.6.1.4.1.15497.1.1.1.13.1.1.1 = INTEGER: 1
+# .1.3.6.1.4.1.15497.1.1.1.13.1.1.2 = INTEGER: 2
+# .1.3.6.1.4.1.15497.1.1.1.13.1.1.3 = INTEGER: 3
+# .1.3.6.1.4.1.15497.1.1.1.13.1.1.4 = INTEGER: 4
+# .1.3.6.1.4.1.15497.1.1.1.13.1.1.5 = INTEGER: 5
+# .1.3.6.1.4.1.15497.1.1.1.13.1.1.6 = INTEGER: 6
+# .1.3.6.1.4.1.15497.1.1.1.13.1.1.7 = INTEGER: 7
+# .1.3.6.1.4.1.15497.1.1.1.13.1.1.8 = INTEGER: 8
+# .1.3.6.1.4.1.15497.1.1.1.13.1.1.9 = INTEGER: 9
+# .1.3.6.1.4.1.15497.1.1.1.13.1.1.10 = INTEGER: 10
+# .1.3.6.1.4.1.15497.1.1.1.13.1.1.11 = INTEGER: 11
+# .1.3.6.1.4.1.15497.1.1.1.13.1.1.12 = INTEGER: 12
+# .1.3.6.1.4.1.15497.1.1.1.13.1.1.13 = INTEGER: 13
+# .1.3.6.1.4.1.15497.1.1.1.13.1.1.14 = INTEGER: 14
+# .1.3.6.1.4.1.15497.1.1.1.13.1.1.15 = INTEGER: 15
+# .1.3.6.1.4.1.15497.1.1.1.13.1.2.1 = STRING: File Reputation
+# .1.3.6.1.4.1.15497.1.1.1.13.1.2.2 = STRING: IronPort Anti-Spam
+# .1.3.6.1.4.1.15497.1.1.1.13.1.2.3 = STRING: McAfee
+# .1.3.6.1.4.1.15497.1.1.1.13.1.2.4 = STRING: Sophos Anti-Virus
+# .1.3.6.1.4.1.15497.1.1.1.13.1.2.5 = STRING: amp
+# .1.3.6.1.4.1.15497.1.1.1.13.1.2.6 = STRING: content_scanner
+# .1.3.6.1.4.1.15497.1.1.1.13.1.2.7 = STRING: enrollment_client
+# .1.3.6.1.4.1.15497.1.1.1.13.1.2.8 = STRING: geo_countries
+# .1.3.6.1.4.1.15497.1.1.1.13.1.2.9 = STRING: howto
+# .1.3.6.1.4.1.15497.1.1.1.13.1.2.10 = STRING: openssh_key
+# .1.3.6.1.4.1.15497.1.1.1.13.1.2.11 = STRING: repeng
+# .1.3.6.1.4.1.15497.1.1.1.13.1.2.12 = STRING: sdr_client
+# .1.3.6.1.4.1.15497.1.1.1.13.1.2.13 = STRING: smart_agent
+# .1.3.6.1.4.1.15497.1.1.1.13.1.2.14 = STRING: support_request
+# .1.3.6.1.4.1.15497.1.1.1.13.1.2.15 = STRING: timezones
+# .1.3.6.1.4.1.15497.1.1.1.13.1.3.1 = Counter32: 1
+# .1.3.6.1.4.1.15497.1.1.1.13.1.3.2 = Counter32: 10
+# .1.3.6.1.4.1.15497.1.1.1.13.1.3.3 = Counter32: 1
+# .1.3.6.1.4.1.15497.1.1.1.13.1.3.4 = Counter32: 1
+# .1.3.6.1.4.1.15497.1.1.1.13.1.3.5 = Counter32: 1
+# .1.3.6.1.4.1.15497.1.1.1.13.1.3.6 = Counter32: 1
+# .1.3.6.1.4.1.15497.1.1.1.13.1.3.7 = Counter32: 1
+# .1.3.6.1.4.1.15497.1.1.1.13.1.3.8 = Counter32: 1
+# .1.3.6.1.4.1.15497.1.1.1.13.1.3.9 = Counter32: 0
+# .1.3.6.1.4.1.15497.1.1.1.13.1.3.10 = Counter32: 0
+# .1.3.6.1.4.1.15497.1.1.1.13.1.3.11 = Counter32: 0
+# .1.3.6.1.4.1.15497.1.1.1.13.1.3.12 = Counter32: 0
+# .1.3.6.1.4.1.15497.1.1.1.13.1.3.13 = Counter32: 0
+# .1.3.6.1.4.1.15497.1.1.1.13.1.3.14 = Counter32: 1
+# .1.3.6.1.4.1.15497.1.1.1.13.1.3.15 = Counter32: 1
+# .1.3.6.1.4.1.15497.1.1.1.13.1.4.1 = Counter32: 0
+# .1.3.6.1.4.1.15497.1.1.1.13.1.4.2 = Counter32: 0
+# .1.3.6.1.4.1.15497.1.1.1.13.1.4.3 = Counter32: 0
+# .1.3.6.1.4.1.15497.1.1.1.13.1.4.4 = Counter32: 0
+# .1.3.6.1.4.1.15497.1.1.1.13.1.4.5 = Counter32: 0
+# .1.3.6.1.4.1.15497.1.1.1.13.1.4.6 = Counter32: 0
+# .1.3.6.1.4.1.15497.1.1.1.13.1.4.7 = Counter32: 0
+# .1.3.6.1.4.1.15497.1.1.1.13.1.4.8 = Counter32: 0
+# .1.3.6.1.4.1.15497.1.1.1.13.1.4.9 = Counter32: 0
+# .1.3.6.1.4.1.15497.1.1.1.13.1.4.10 = Counter32: 0
+# .1.3.6.1.4.1.15497.1.1.1.13.1.4.11 = Counter32: 0
+# .1.3.6.1.4.1.15497.1.1.1.13.1.4.12 = Counter32: 0
+# .1.3.6.1.4.1.15497.1.1.1.13.1.4.13 = Counter32: 0
+# .1.3.6.1.4.1.15497.1.1.1.13.1.4.14 = Counter32: 0
+# .1.3.6.1.4.1.15497.1.1.1.13.1.4.15 = Counter32: 0
+#
+# OMD[cmk16x]:~$ snmpwalk -v2c -c public -m ASYNCOS-MAIL-MIB localhost updateEntry
+# ASYNCOS-MAIL-MIB::updateIndex.1 = INTEGER: 1
+# ASYNCOS-MAIL-MIB::updateIndex.2 = INTEGER: 2
+# ASYNCOS-MAIL-MIB::updateIndex.3 = INTEGER: 3
+# ASYNCOS-MAIL-MIB::updateIndex.4 = INTEGER: 4
+# ASYNCOS-MAIL-MIB::updateIndex.5 = INTEGER: 5
+# ASYNCOS-MAIL-MIB::updateIndex.6 = INTEGER: 6
+# ASYNCOS-MAIL-MIB::updateIndex.7 = INTEGER: 7
+# ASYNCOS-MAIL-MIB::updateIndex.8 = INTEGER: 8
+# ASYNCOS-MAIL-MIB::updateIndex.9 = INTEGER: 9
+# ASYNCOS-MAIL-MIB::updateIndex.10 = INTEGER: 10
+# ASYNCOS-MAIL-MIB::updateIndex.11 = INTEGER: 11
+# ASYNCOS-MAIL-MIB::updateIndex.12 = INTEGER: 12
+# ASYNCOS-MAIL-MIB::updateIndex.13 = INTEGER: 13
+# ASYNCOS-MAIL-MIB::updateIndex.14 = INTEGER: 14
+# ASYNCOS-MAIL-MIB::updateIndex.15 = INTEGER: 15
+# ASYNCOS-MAIL-MIB::updateServiceName.1 = STRING: File Reputation
+# ASYNCOS-MAIL-MIB::updateServiceName.2 = STRING: IronPort Anti-Spam
+# ASYNCOS-MAIL-MIB::updateServiceName.3 = STRING: McAfee
+# ASYNCOS-MAIL-MIB::updateServiceName.4 = STRING: Sophos Anti-Virus
+# ASYNCOS-MAIL-MIB::updateServiceName.5 = STRING: amp
+# ASYNCOS-MAIL-MIB::updateServiceName.6 = STRING: content_scanner
+# ASYNCOS-MAIL-MIB::updateServiceName.7 = STRING: enrollment_client
+# ASYNCOS-MAIL-MIB::updateServiceName.8 = STRING: geo_countries
+# ASYNCOS-MAIL-MIB::updateServiceName.9 = STRING: howto
+# ASYNCOS-MAIL-MIB::updateServiceName.10 = STRING: openssh_key
+# ASYNCOS-MAIL-MIB::updateServiceName.11 = STRING: repeng
+# ASYNCOS-MAIL-MIB::updateServiceName.12 = STRING: sdr_client
+# ASYNCOS-MAIL-MIB::updateServiceName.13 = STRING: smart_agent
+# ASYNCOS-MAIL-MIB::updateServiceName.14 = STRING: support_request
+# ASYNCOS-MAIL-MIB::updateServiceName.15 = STRING: timezones
+# ASYNCOS-MAIL-MIB::updates.1 = Counter32: 1
+# ASYNCOS-MAIL-MIB::updates.2 = Counter32: 10
+# ASYNCOS-MAIL-MIB::updates.3 = Counter32: 1
+# ASYNCOS-MAIL-MIB::updates.4 = Counter32: 1
+# ASYNCOS-MAIL-MIB::updates.5 = Counter32: 1
+# ASYNCOS-MAIL-MIB::updates.6 = Counter32: 1
+# ASYNCOS-MAIL-MIB::updates.7 = Counter32: 1
+# ASYNCOS-MAIL-MIB::updates.8 = Counter32: 1
+# ASYNCOS-MAIL-MIB::updates.9 = Counter32: 0
+# ASYNCOS-MAIL-MIB::updates.10 = Counter32: 0
+# ASYNCOS-MAIL-MIB::updates.11 = Counter32: 0
+# ASYNCOS-MAIL-MIB::updates.12 = Counter32: 0
+# ASYNCOS-MAIL-MIB::updates.13 = Counter32: 0
+# ASYNCOS-MAIL-MIB::updates.14 = Counter32: 1
+# ASYNCOS-MAIL-MIB::updates.15 = Counter32: 1
+# ASYNCOS-MAIL-MIB::updateFailures.1 = Counter32: 0
+# ASYNCOS-MAIL-MIB::updateFailures.2 = Counter32: 0
+# ASYNCOS-MAIL-MIB::updateFailures.3 = Counter32: 0
+# ASYNCOS-MAIL-MIB::updateFailures.4 = Counter32: 0
+# ASYNCOS-MAIL-MIB::updateFailures.5 = Counter32: 0
+# ASYNCOS-MAIL-MIB::updateFailures.6 = Counter32: 0
+# ASYNCOS-MAIL-MIB::updateFailures.7 = Counter32: 0
+# ASYNCOS-MAIL-MIB::updateFailures.8 = Counter32: 0
+# ASYNCOS-MAIL-MIB::updateFailures.9 = Counter32: 0
+# ASYNCOS-MAIL-MIB::updateFailures.10 = Counter32: 0
+# ASYNCOS-MAIL-MIB::updateFailures.11 = Counter32: 0
+# ASYNCOS-MAIL-MIB::updateFailures.12 = Counter32: 0
+# ASYNCOS-MAIL-MIB::updateFailures.13 = Counter32: 0
+# ASYNCOS-MAIL-MIB::updateFailures.14 = Counter32: 0
+# ASYNCOS-MAIL-MIB::updateFailures.15 = Counter32: 0
+#
+from typing import Mapping, List, NamedTuple
+
+from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
+    DiscoveryResult,
+    CheckResult,
+    StringTable,
+)
+
+from cmk.base.plugins.agent_based.agent_based_api.v1 import (
+    register,
+    Service,
+    check_levels,
+    Result,
+    SNMPTree,
+    contains,
+    State,
+    render,
+)
+
+
+class CiscoAsyncosUpdate(NamedTuple):
+    updates: int
+    updatefailures: int
+
+# [[
+#   ['File Reputation', '6', '38'],
+#   ['IronPort Anti-Spam', '12', '9'],
+#   ['McAfee', '6', '25'],
+#   ['Sophos Anti-Virus', '6', '17'],
+#   ['amp', '1', '0'],
+#   ['content_scanner', '1', '0'],
+#   ['enrollment_client', '1', '0'],
+#   ['geo_countries', '1', '0'],
+#   ['howto', '0', '0'],
+#   ['openssh_key', '0', '0'],
+#   ['repeng', '0', '0'],
+#   ['sdr_client', '0', '0'],
+#   ['smart_agent', '0', '0'],
+#   ['support_request', '1', '0'],
+#   ['timezones', '1', '0']
+# ]]
+def parse_cisco_asyncos_update(string_table: List[StringTable]) -> Mapping[str, CiscoAsyncosUpdate]:
+    features = {}
+    for feature, updates, update_failures in string_table[0]:
+        features.update({feature: CiscoAsyncosUpdate(
+            updates=int(updates),
+            updatefailures=int(update_failures)
+        ) })
+    return features
+
+# {
+#  'File Reputation': CiscoAsyncosUpdate(updates=6, updatefailures=38),
+#  'IronPort Anti-Spam': CiscoAsyncosUpdate(updates=12, updatefailures=9),
+#  'McAfee': CiscoAsyncosUpdate(updates=6, updatefailures=25),
+#  'Sophos Anti-Virus': CiscoAsyncosUpdate(updates=6, updatefailures=17),
+#  'amp': CiscoAsyncosUpdate(updates=1, updatefailures=0),
+#  'content_scanner': CiscoAsyncosUpdate(updates=1, updatefailures=0),
+#  'enrollment_client': CiscoAsyncosUpdate(updates=1, updatefailures=0),
+#  'geo_countries': CiscoAsyncosUpdate(updates=1, updatefailures=0),
+#  'howto': CiscoAsyncosUpdate(updates=0, updatefailures=0),
+#  'openssh_key': CiscoAsyncosUpdate(updates=0, updatefailures=0),
+#  'repeng': CiscoAsyncosUpdate(updates=0, updatefailures=0),
+#  'sdr_client': CiscoAsyncosUpdate(updates=0, updatefailures=0),
+#  'smart_agent': CiscoAsyncosUpdate(updates=0, updatefailures=0),
+#  'support_request': CiscoAsyncosUpdate(updates=1, updatefailures=0),
+#  'timezones': CiscoAsyncosUpdate(updates=1, updatefailures=0)
+# }
+def discovery_cisco_asyncos_update(section:Mapping[str, CiscoAsyncosUpdate])-> DiscoveryResult:
+    yield Service()
+
+def check_cisco_asyncos_update(params, section:Mapping[str, CiscoAsyncosUpdate]) -> CheckResult:
+    features_ignore = params.get('features_ignore', [])
+    warn, crit = params.get('failedLevel')
+    ignore_count = 0
+    for feature in section.keys():
+        if feature in features_ignore:
+            yield Result(state=State.OK, notice='Feature %s: %d/%d updates/update failures. Feature ignored!' % (feature, section[feature].updates, section[feature].updatefailures))
+            ignore_count += 1
+        else:
+            yield Result(state=State.OK, notice='Feature %s: %d/%d updates/update failures. Feature ignored!' % (feature, section[feature].updates, section[feature].updatefailures))
+
+    yield Result(state=State.OK, summary='%d/%d Features found/ignored' % (len(section.keys()), ignore_count))
+
+    # if len(info) > 0:
+    #     infotext = ''
+    #     longoutput = ''
+    #     perfdata = []
+    #     failedItemsWarn = []
+    #     failedItemsCrit = []
+    #     failedWarn, failedCrit = params.get('failedLevel')
+    #     features_ignore = params['features_ignore']
+    #     lastState = 1
+    #
+    #     for line in info:
+    #         name, passed, failed = line
+    #         longoutput += '\n%s: %s/%s passed/failed attempt(s)' % (name, passed, failed)
+    #         # read counters
+    #         passedLast = get_item_state('cisco_asyncos_update_%s_passedLast' % name)
+    #         failedLast = get_item_state('cisco_asyncos_update_%s_failedLast' % name)
+    #         failedAttempts = get_item_state('cisco_asyncos_update_%s_failedAttempts' % name)
+    #
+    #         if (passedLast == None) or (failedLast == None) or (failedAttempts == None):  # or (lastState == None):
+    #             # init counters
+    #             set_item_state('cisco_asyncos_update_%s_passedLast' % name, passed)
+    #             set_item_state('cisco_asyncos_update_%s_failedLast' % name, failed)
+    #             set_item_state('cisco_asyncos_update_%s_failedAttempts' % name, 0)
+    #         else:
+    #             set_item_state('cisco_asyncos_update_%s_passedLast' % name, passed)
+    #             set_item_state('cisco_asyncos_update_%s_failedLast' % name, failed)
+    #             passedLast = int(passedLast)
+    #             failedLast = int(failedLast)
+    #             failedAttempts = int(failedAttempts)
+    #             failed = int(failed)
+    #             passed = int(passed)
+    #             # reset counter if overrun
+    #             if failed < failedLast:
+    #                 set_item_state('cisco_asyncos_update_%s_failedLast' % name, failed)
+    #                 failedLast = failed
+    #             if passed < passedLast:
+    #                 set_item_state('cisco_asyncos_update_%s_passedLast' % name, passed)
+    #                 passedLast = passed
+    #
+    #             if passed > passedLast:
+    #                 # rest error counter after passed update attempt
+    #                 set_item_state('cisco_asyncos_update_%s_failedAttempts' % name, 0)
+    #             else:
+    #                 failedAttempts = failedAttempts + failed - failedLast
+    #                 set_item_state('cisco_asyncos_update_%s_failedAttempts' % name, failedAttempts)
+    #                 if name not in features_ignore:
+    #                     if failedAttempts >= failedCrit:
+    #                         failedItemsCrit.append(name)
+    #                         lastState = -1
+    #                     elif failedAttempts >= failedWarn:
+    #                         failedItemsWarn.append(name)
+    #                         lastState = -1
+    #
+    #         perfdata.append((name.replace(' ', '_'), lastState, None, None, -1, 1))
+    #
+    #     infotext += '%d item(s) found' % len(info)
+    #     if len(failedItemsCrit) > 0:
+    #         yield 2, '%d failed item(s) (%s), failed attempts >= %d' % (len(failedItemsCrit), ', '.join(failedItemsCrit), failedCrit)
+    #     if len(failedItemsWarn) > 0:
+    #         yield 1, '%d failed item(s) (%s), failed attempts >= %d' % (len(failedItemsWarn), ', '.join(failedItemsWarn), failedWarn)
+    #
+    #     yield 0, infotext + longoutput, perfdata
+
+
+
+register.snmp_section(
+    name='cisco_asyncos_update',
+    parse_function=parse_cisco_asyncos_update,
+    fetch=[
+        SNMPTree(
+            base='.1.3.6.1.4.1.15497.1.1.1.13.1',  # ASYNCOS-MAIL-MIB::updateEntry
+            oids=[
+                '2',  # updateServiceName -> A textual name for an update entry
+                '3',  # updates -> The number of successful attempts that have occurred when updating a service
+                '4',  # updateFailures -> "The number of failed attempts that have occurred when updating a service
+            ]
+        ),
+    ],
+    detect=contains('.1.3.6.1.2.1.1.1.0', 'AsyncOS'),
+)
+
+register.check_plugin(
+    name='cisco_asyncos_update',
+    service_name='Update',
+    discovery_function=discovery_cisco_asyncos_update,
+    check_function=check_cisco_asyncos_update,
+    check_default_parameters={'failedLevel': (5, 10)},
+    check_ruleset_name='cisco_asyncos_update',
+)
+
diff --git a/checks/cisco_asyncos_update b/checks/cisco_asyncos_update
deleted file mode 100644
index 333795a..0000000
--- a/checks/cisco_asyncos_update
+++ /dev/null
@@ -1,259 +0,0 @@
-#!/usr/bin/python
-# -*- encoding: utf-8; py-indent-offset: 4 -*-
-#
-# Rewriten by: Th.L.
-# Date: 19-02-2020
-#
-# 2020-05-14: added wato oprion to ignore items
-#
-# check_mk is free software;  you can redistribute it and/or modify it
-# under the  terms of the  GNU General Public License  as published by
-# the Free Software Foundation in version 2.  check_mk is  distributed
-# in the hope that it will be useful, but WITHOUT ANY WARRANTY;  with-
-# out even the implied warranty of  MERCHANTABILITY  or  FITNESS FOR A
-# PARTICULAR PURPOSE. See the  GNU General Public License for more de-
-# ails.  You should have  received  a copy of the  GNU  General Public
-# License along with GNU Make; see the file  COPYING.  If  not,  write
-# to the Free Software Foundation, Inc., 51 Franklin St,  Fifth Floor,
-# Boston, MA 02110-1301 USA.
-#
-#
-# sample snmpwalk
-#
-# OMD[cmk16x]:~$ snmpwalk -v2c -c public -m ASYNCOS-MAIL-MIB -ObentU localhost updateEntry
-# .1.3.6.1.4.1.15497.1.1.1.13.1.1.1 = INTEGER: 1
-# .1.3.6.1.4.1.15497.1.1.1.13.1.1.2 = INTEGER: 2
-# .1.3.6.1.4.1.15497.1.1.1.13.1.1.3 = INTEGER: 3
-# .1.3.6.1.4.1.15497.1.1.1.13.1.1.4 = INTEGER: 4
-# .1.3.6.1.4.1.15497.1.1.1.13.1.1.5 = INTEGER: 5
-# .1.3.6.1.4.1.15497.1.1.1.13.1.1.6 = INTEGER: 6
-# .1.3.6.1.4.1.15497.1.1.1.13.1.1.7 = INTEGER: 7
-# .1.3.6.1.4.1.15497.1.1.1.13.1.1.8 = INTEGER: 8
-# .1.3.6.1.4.1.15497.1.1.1.13.1.1.9 = INTEGER: 9
-# .1.3.6.1.4.1.15497.1.1.1.13.1.1.10 = INTEGER: 10
-# .1.3.6.1.4.1.15497.1.1.1.13.1.1.11 = INTEGER: 11
-# .1.3.6.1.4.1.15497.1.1.1.13.1.1.12 = INTEGER: 12
-# .1.3.6.1.4.1.15497.1.1.1.13.1.1.13 = INTEGER: 13
-# .1.3.6.1.4.1.15497.1.1.1.13.1.1.14 = INTEGER: 14
-# .1.3.6.1.4.1.15497.1.1.1.13.1.1.15 = INTEGER: 15
-# .1.3.6.1.4.1.15497.1.1.1.13.1.2.1 = STRING: File Reputation
-# .1.3.6.1.4.1.15497.1.1.1.13.1.2.2 = STRING: IronPort Anti-Spam
-# .1.3.6.1.4.1.15497.1.1.1.13.1.2.3 = STRING: McAfee
-# .1.3.6.1.4.1.15497.1.1.1.13.1.2.4 = STRING: Sophos Anti-Virus
-# .1.3.6.1.4.1.15497.1.1.1.13.1.2.5 = STRING: amp
-# .1.3.6.1.4.1.15497.1.1.1.13.1.2.6 = STRING: content_scanner
-# .1.3.6.1.4.1.15497.1.1.1.13.1.2.7 = STRING: enrollment_client
-# .1.3.6.1.4.1.15497.1.1.1.13.1.2.8 = STRING: geo_countries
-# .1.3.6.1.4.1.15497.1.1.1.13.1.2.9 = STRING: howto
-# .1.3.6.1.4.1.15497.1.1.1.13.1.2.10 = STRING: openssh_key
-# .1.3.6.1.4.1.15497.1.1.1.13.1.2.11 = STRING: repeng
-# .1.3.6.1.4.1.15497.1.1.1.13.1.2.12 = STRING: sdr_client
-# .1.3.6.1.4.1.15497.1.1.1.13.1.2.13 = STRING: smart_agent
-# .1.3.6.1.4.1.15497.1.1.1.13.1.2.14 = STRING: support_request
-# .1.3.6.1.4.1.15497.1.1.1.13.1.2.15 = STRING: timezones
-# .1.3.6.1.4.1.15497.1.1.1.13.1.3.1 = Counter32: 1
-# .1.3.6.1.4.1.15497.1.1.1.13.1.3.2 = Counter32: 10
-# .1.3.6.1.4.1.15497.1.1.1.13.1.3.3 = Counter32: 1
-# .1.3.6.1.4.1.15497.1.1.1.13.1.3.4 = Counter32: 1
-# .1.3.6.1.4.1.15497.1.1.1.13.1.3.5 = Counter32: 1
-# .1.3.6.1.4.1.15497.1.1.1.13.1.3.6 = Counter32: 1
-# .1.3.6.1.4.1.15497.1.1.1.13.1.3.7 = Counter32: 1
-# .1.3.6.1.4.1.15497.1.1.1.13.1.3.8 = Counter32: 1
-# .1.3.6.1.4.1.15497.1.1.1.13.1.3.9 = Counter32: 0
-# .1.3.6.1.4.1.15497.1.1.1.13.1.3.10 = Counter32: 0
-# .1.3.6.1.4.1.15497.1.1.1.13.1.3.11 = Counter32: 0
-# .1.3.6.1.4.1.15497.1.1.1.13.1.3.12 = Counter32: 0
-# .1.3.6.1.4.1.15497.1.1.1.13.1.3.13 = Counter32: 0
-# .1.3.6.1.4.1.15497.1.1.1.13.1.3.14 = Counter32: 1
-# .1.3.6.1.4.1.15497.1.1.1.13.1.3.15 = Counter32: 1
-# .1.3.6.1.4.1.15497.1.1.1.13.1.4.1 = Counter32: 0
-# .1.3.6.1.4.1.15497.1.1.1.13.1.4.2 = Counter32: 0
-# .1.3.6.1.4.1.15497.1.1.1.13.1.4.3 = Counter32: 0
-# .1.3.6.1.4.1.15497.1.1.1.13.1.4.4 = Counter32: 0
-# .1.3.6.1.4.1.15497.1.1.1.13.1.4.5 = Counter32: 0
-# .1.3.6.1.4.1.15497.1.1.1.13.1.4.6 = Counter32: 0
-# .1.3.6.1.4.1.15497.1.1.1.13.1.4.7 = Counter32: 0
-# .1.3.6.1.4.1.15497.1.1.1.13.1.4.8 = Counter32: 0
-# .1.3.6.1.4.1.15497.1.1.1.13.1.4.9 = Counter32: 0
-# .1.3.6.1.4.1.15497.1.1.1.13.1.4.10 = Counter32: 0
-# .1.3.6.1.4.1.15497.1.1.1.13.1.4.11 = Counter32: 0
-# .1.3.6.1.4.1.15497.1.1.1.13.1.4.12 = Counter32: 0
-# .1.3.6.1.4.1.15497.1.1.1.13.1.4.13 = Counter32: 0
-# .1.3.6.1.4.1.15497.1.1.1.13.1.4.14 = Counter32: 0
-# .1.3.6.1.4.1.15497.1.1.1.13.1.4.15 = Counter32: 0
-#
-# OMD[cmk16x]:~$ snmpwalk -v2c -c public -m ASYNCOS-MAIL-MIB localhost updateEntry
-# ASYNCOS-MAIL-MIB::updateIndex.1 = INTEGER: 1
-# ASYNCOS-MAIL-MIB::updateIndex.2 = INTEGER: 2
-# ASYNCOS-MAIL-MIB::updateIndex.3 = INTEGER: 3
-# ASYNCOS-MAIL-MIB::updateIndex.4 = INTEGER: 4
-# ASYNCOS-MAIL-MIB::updateIndex.5 = INTEGER: 5
-# ASYNCOS-MAIL-MIB::updateIndex.6 = INTEGER: 6
-# ASYNCOS-MAIL-MIB::updateIndex.7 = INTEGER: 7
-# ASYNCOS-MAIL-MIB::updateIndex.8 = INTEGER: 8
-# ASYNCOS-MAIL-MIB::updateIndex.9 = INTEGER: 9
-# ASYNCOS-MAIL-MIB::updateIndex.10 = INTEGER: 10
-# ASYNCOS-MAIL-MIB::updateIndex.11 = INTEGER: 11
-# ASYNCOS-MAIL-MIB::updateIndex.12 = INTEGER: 12
-# ASYNCOS-MAIL-MIB::updateIndex.13 = INTEGER: 13
-# ASYNCOS-MAIL-MIB::updateIndex.14 = INTEGER: 14
-# ASYNCOS-MAIL-MIB::updateIndex.15 = INTEGER: 15
-# ASYNCOS-MAIL-MIB::updateServiceName.1 = STRING: File Reputation
-# ASYNCOS-MAIL-MIB::updateServiceName.2 = STRING: IronPort Anti-Spam
-# ASYNCOS-MAIL-MIB::updateServiceName.3 = STRING: McAfee
-# ASYNCOS-MAIL-MIB::updateServiceName.4 = STRING: Sophos Anti-Virus
-# ASYNCOS-MAIL-MIB::updateServiceName.5 = STRING: amp
-# ASYNCOS-MAIL-MIB::updateServiceName.6 = STRING: content_scanner
-# ASYNCOS-MAIL-MIB::updateServiceName.7 = STRING: enrollment_client
-# ASYNCOS-MAIL-MIB::updateServiceName.8 = STRING: geo_countries
-# ASYNCOS-MAIL-MIB::updateServiceName.9 = STRING: howto
-# ASYNCOS-MAIL-MIB::updateServiceName.10 = STRING: openssh_key
-# ASYNCOS-MAIL-MIB::updateServiceName.11 = STRING: repeng
-# ASYNCOS-MAIL-MIB::updateServiceName.12 = STRING: sdr_client
-# ASYNCOS-MAIL-MIB::updateServiceName.13 = STRING: smart_agent
-# ASYNCOS-MAIL-MIB::updateServiceName.14 = STRING: support_request
-# ASYNCOS-MAIL-MIB::updateServiceName.15 = STRING: timezones
-# ASYNCOS-MAIL-MIB::updates.1 = Counter32: 1
-# ASYNCOS-MAIL-MIB::updates.2 = Counter32: 10
-# ASYNCOS-MAIL-MIB::updates.3 = Counter32: 1
-# ASYNCOS-MAIL-MIB::updates.4 = Counter32: 1
-# ASYNCOS-MAIL-MIB::updates.5 = Counter32: 1
-# ASYNCOS-MAIL-MIB::updates.6 = Counter32: 1
-# ASYNCOS-MAIL-MIB::updates.7 = Counter32: 1
-# ASYNCOS-MAIL-MIB::updates.8 = Counter32: 1
-# ASYNCOS-MAIL-MIB::updates.9 = Counter32: 0
-# ASYNCOS-MAIL-MIB::updates.10 = Counter32: 0
-# ASYNCOS-MAIL-MIB::updates.11 = Counter32: 0
-# ASYNCOS-MAIL-MIB::updates.12 = Counter32: 0
-# ASYNCOS-MAIL-MIB::updates.13 = Counter32: 0
-# ASYNCOS-MAIL-MIB::updates.14 = Counter32: 1
-# ASYNCOS-MAIL-MIB::updates.15 = Counter32: 1
-# ASYNCOS-MAIL-MIB::updateFailures.1 = Counter32: 0
-# ASYNCOS-MAIL-MIB::updateFailures.2 = Counter32: 0
-# ASYNCOS-MAIL-MIB::updateFailures.3 = Counter32: 0
-# ASYNCOS-MAIL-MIB::updateFailures.4 = Counter32: 0
-# ASYNCOS-MAIL-MIB::updateFailures.5 = Counter32: 0
-# ASYNCOS-MAIL-MIB::updateFailures.6 = Counter32: 0
-# ASYNCOS-MAIL-MIB::updateFailures.7 = Counter32: 0
-# ASYNCOS-MAIL-MIB::updateFailures.8 = Counter32: 0
-# ASYNCOS-MAIL-MIB::updateFailures.9 = Counter32: 0
-# ASYNCOS-MAIL-MIB::updateFailures.10 = Counter32: 0
-# ASYNCOS-MAIL-MIB::updateFailures.11 = Counter32: 0
-# ASYNCOS-MAIL-MIB::updateFailures.12 = Counter32: 0
-# ASYNCOS-MAIL-MIB::updateFailures.13 = Counter32: 0
-# ASYNCOS-MAIL-MIB::updateFailures.14 = Counter32: 0
-# ASYNCOS-MAIL-MIB::updateFailures.15 = Counter32: 0
-#
-# sample info
-#
-# [[u'File Reputation', u'1', u'0'],
-#  [u'IronPort Anti-Spam', u'10', u'0'],
-#  [u'McAfee', u'1', u'0'],
-#  [u'Sophos Anti-Virus', u'1', u'0'],
-#  [u'amp', u'1', u'0'],
-#  [u'content_scanner', u'1', u'0'],
-#  [u'enrollment_client', u'1', u'0'],
-#  [u'geo_countries', u'1', u'0'],
-#  [u'howto', u'0', u'0'],
-#  [u'openssh_key', u'0', u'0'],
-#  [u'repeng', u'0', u'0'],
-#  [u'sdr_client', u'0', u'0'],
-#  [u'smart_agent', u'0', u'0'],
-#  [u'support_request', u'1', u'0'],
-#  [u'timezones', u'1', u'0']
-# ]
-#
-
-factory_settings['cisco_asyncos_update_default_levels'] = {
-    'failedLevel': (5, 10),
-    'features_ignore': []
-}
-
-
-def inventory_cisco_asyncos_update(info):
-    if len(info) > 0:
-        return [(None, None)]
-
-
-def check_cisco_asyncos_update(_no_item, params, info):
-    if len(info) > 0:           
-        infotext = ''
-        longoutput = ''
-        perfdata = []
-        failedItemsWarn = []
-        failedItemsCrit = []
-        failedWarn, failedCrit = params.get('failedLevel')
-        features_ignore = params['features_ignore']
-        lastState = 1
-        
-        for line in info:
-            name, passed, failed = line
-            longoutput += '\n%s: %s/%s passed/failed attempt(s)' % (name, passed, failed)
-            # read counters
-            passedLast = get_item_state('cisco_asyncos_update_%s_passedLast' % name)
-            failedLast = get_item_state('cisco_asyncos_update_%s_failedLast' % name)
-            failedAttempts = get_item_state('cisco_asyncos_update_%s_failedAttempts' % name)
-
-            if (passedLast == None) or (failedLast == None) or (failedAttempts == None):  # or (lastState == None):
-                # init counters
-                set_item_state('cisco_asyncos_update_%s_passedLast' % name, passed)
-                set_item_state('cisco_asyncos_update_%s_failedLast' % name, failed)
-                set_item_state('cisco_asyncos_update_%s_failedAttempts' % name, 0)
-            else:
-                set_item_state('cisco_asyncos_update_%s_passedLast' % name, passed)
-                set_item_state('cisco_asyncos_update_%s_failedLast' % name, failed)
-                passedLast = int(passedLast)
-                failedLast = int(failedLast)
-                failedAttempts = int(failedAttempts)
-                failed = int(failed)
-                passed = int(passed)
-                # reset counter if overrun
-                if failed < failedLast:
-                    set_item_state('cisco_asyncos_update_%s_failedLast' % name, failed)
-                    failedLast = failed
-                if passed < passedLast:
-                    set_item_state('cisco_asyncos_update_%s_passedLast' % name, passed)
-                    passedLast = passed
-
-                if passed > passedLast:
-                    # rest error counter after passed update attempt
-                    set_item_state('cisco_asyncos_update_%s_failedAttempts' % name, 0)
-                else:
-                    failedAttempts = failedAttempts + failed - failedLast
-                    set_item_state('cisco_asyncos_update_%s_failedAttempts' % name, failedAttempts)
-                    if name not in features_ignore:
-                        if failedAttempts >= failedCrit:
-                            failedItemsCrit.append(name)
-                            lastState = -1
-                        elif failedAttempts >= failedWarn:
-                            failedItemsWarn.append(name)
-                            lastState = -1
-
-            perfdata.append((name.replace(' ', '_'), lastState, None, None, -1, 1))
-
-        infotext += '%d item(s) found' % len(info)
-        if len(failedItemsCrit) > 0:            
-            yield 2, '%d failed item(s) (%s), failed attempts >= %d' % (len(failedItemsCrit), ', '.join(failedItemsCrit), failedCrit)
-        if len(failedItemsWarn) > 0:
-            yield 1, '%d failed item(s) (%s), failed attempts >= %d' % (len(failedItemsWarn), ', '.join(failedItemsWarn), failedWarn)
-
-        yield 0, infotext + longoutput, perfdata
-
-
-check_info['cisco_asyncos_update'] = {
-    'check_function':          check_cisco_asyncos_update,
-    'inventory_function':      inventory_cisco_asyncos_update,
-    'group':                   'cisco_asyncos_update',
-    'service_description':     'Update',
-    'has_perfdata': True,
-    'snmp_info':               ('.1.3.6.1.4.1.15497.1.1.1.13.1', [  # ASYNCOS-MAIL-MIB::updateEntry
-                                '2',  # updateServiceName --> A textual name for an update entry
-                                '3',  # updates --> The number of successful attempts that have occurred when updating a service
-                                '4',  # updateFailures --> "The number of failed attempts that have occurred when updating a service
-                                ]),
-    'default_levels_variable': 'cisco_asyncos_update_default_levels',
-    'snmp_scan_function':  lambda oid: oid('.1.3.6.1.2.1.1.1.0').find('AsyncOS') != -1,
-    # 'snmp_scan_function': scan_cisco_asyncos,
-    # 'includes': ['cisco_asyncos.include'],
-}
-
diff --git a/cisco_asyncos.mkp b/cisco_asyncos.mkp
index 52a40594eff84cafd40e7eaa4496193b65e870ba..fa793cf65e90013d6fbb159691d48f2f33cc7338 100644
GIT binary patch
literal 11591
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
RwT}>JUWX+js8(pG{{hOfF8}}l

literal 11974
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
N0s=btU~S-F{}1Y?u4@1Q

diff --git a/packages/cisco_asyncos b/packages/cisco_asyncos
index 96321b6..0919172 100644
--- a/packages/cisco_asyncos
+++ b/packages/cisco_asyncos
@@ -37,8 +37,8 @@
                            'cisco_asyncos_messageage.py',
                            'cisco_asyncos_queue.py',
                            'cisco_asyncos_resources.py',
-                           'cisco_asyncos_license.py'],
-           'checks': ['cisco_asyncos_update'],
+                           'cisco_asyncos_license.py',
+                           'cisco_asyncos_update.py'],
            'web': ['plugins/wato/cisco_asyncos_license.py',
                    'plugins/wato/cisco_asyncos_queue.py',
                    'plugins/wato/cisco_asyncos_update.py',
diff --git a/web/plugins/wato/cisco_asyncos_queue.py b/web/plugins/wato/cisco_asyncos_queue.py
index d3bf147..5b3722c 100644
--- a/web/plugins/wato/cisco_asyncos_queue.py
+++ b/web/plugins/wato/cisco_asyncos_queue.py
@@ -1,23 +1,41 @@
-#!/usr/bin/python
-# -*- encoding: utf-8; py-indent-offset: 4 -*-
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
 #
 #
-register_check_parameters(
-    subgroup_applications,
-    'cisco_asyncos_queue',
-    _('Cisco AsyncOS queue'),
-    Dictionary(
-       #  help=_(''),
-        elements=[
-            ('levels',
-             Tuple(
-                 title=_('Levels for nuber of messages in work queue'),
-                 elements=[
-                     Integer(title=_('Warning'), default_value=50, unit=_('# of messages')),
-                     Integer(title=_('Critical'), default_value=100, unit=_('# of messages')),
-                 ])),
-        ],
-    ),
-    None,
-    match_type='dict',
+from cmk.gui.i18n import _
+from cmk.gui.valuespec import (
+    Dictionary,
+    Integer,
+    TextAscii,
+    ListOfStrings,
+    Tuple,
 )
+
+from cmk.gui.plugins.wato import (
+    CheckParameterRulespecWithItem,
+    rulespec_registry,
+    RulespecGroupCheckParametersNetworking,
+)
+
+
+def _parameter_valuespec_cisco_asyncos_queue():
+    return Dictionary(elements=[
+        ('levels',
+         Tuple(
+             title=_('Levels for nuber of messages in work queue'),
+             elements=[
+                 Integer(title=_('Warning'), default_value=50, unit=_('# of messages')),
+                 Integer(title=_('Critical'), default_value=100, unit=_('# of messages')),
+             ])),
+    ])
+
+
+rulespec_registry.register(
+    CheckParameterRulespecWithItem(
+        check_group_name='cisco_asyncos_queue',
+        group=RulespecGroupCheckParametersNetworking,
+        item_spec=lambda: TextAscii(title=_('Cisco AsyncOS queue'), ),
+        match_type='dict',
+        parameter_valuespec=_parameter_valuespec_cisco_asyncos_queue,
+        title=lambda: _('Cisco AsyncOS queue'),
+    ))
diff --git a/web/plugins/wato/cisco_asyncos_update.py b/web/plugins/wato/cisco_asyncos_update.py
index 6701443..930d2a9 100644
--- a/web/plugins/wato/cisco_asyncos_update.py
+++ b/web/plugins/wato/cisco_asyncos_update.py
@@ -1,31 +1,49 @@
-#!/usr/bin/python
-# -*- encoding: utf-8; py-indent-offset: 4 -*-
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
 #
 #
-register_check_parameters(
-    subgroup_applications,
-    'cisco_asyncos_update',
-    _('Cisco AsyncOS update'),
-    Dictionary(
-       #  help=_(''),
-        elements=[
-            ('features_ignore',
-              ListOfStrings(
-                  title=_('update features to ignore'),
-                  orientation='vertical',
-                  help=_('there will be no warning/critical if this features are not updated'
-                         'Examples: geo_countries, timezones, etc.'),
-              )
-             ),
-            ('failedLevel',
-             Tuple(
-                 title=_('Levels for failed attempts'),
-                 elements=[
-                     Integer(title=_('Warning'), default_value=5, unit=_('# of failed attempts')),
-                     Integer(title=_('Critical'), default_value=10, unit=_('# of failed attempts')),
-                 ])),
-        ],
-    ),
-    None,
-    match_type='dict',
+from cmk.gui.i18n import _
+from cmk.gui.valuespec import (
+    Dictionary,
+    Integer,
+    TextAscii,
+    ListOfStrings,
+    Tuple,
 )
+
+from cmk.gui.plugins.wato import (
+    CheckParameterRulespecWithItem,
+    rulespec_registry,
+    RulespecGroupCheckParametersNetworking,
+)
+
+
+def _parameter_valuespec_cisco_asyncos_license():
+    return Dictionary(elements=[
+        ('features_ignore',
+         ListOfStrings(
+             title=_('Update features to ignore'),
+             orientation='vertical',
+             help=_('there will be no warning/critical if this features are not updated'
+                    'Examples: geo_countries, timezones, etc.'),
+         )
+         ),
+        ('failedLevel',
+         Tuple(
+             title=_('Levels for failed attempts'),
+             elements=[
+                 Integer(title=_('Warning'), default_value=5, unit=_('# of failed attempts')),
+                 Integer(title=_('Critical'), default_value=10, unit=_('# of failed attempts')),
+             ])),
+    ])
+
+
+rulespec_registry.register(
+    CheckParameterRulespecWithItem(
+        check_group_name='cisco_asyncos_update',
+        group=RulespecGroupCheckParametersNetworking,
+        item_spec=lambda: TextAscii(title=_('Cisco AsyncOS update'), ),
+        match_type='dict',
+        parameter_valuespec=_parameter_valuespec_cisco_asyncos_update,
+        title=lambda: _('Cisco AsyncOS update'),
+    ))
-- 
GitLab