diff --git a/agent_based/checkpoint_raid.py b/agent_based/checkpoint_raid.py new file mode 100644 index 0000000000000000000000000000000000000000..3208552f2f21c86b2ed17732c39346d14e58f482 --- /dev/null +++ b/agent_based/checkpoint_raid.py @@ -0,0 +1,374 @@ +#!/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 : 2016-05-31 +# +# monitor Check Point appliance soft RAID +# +# 2018-03-08: changed snmp scan function +# 2018-03-13: code cleanup +# 2018-03-15: more code cleanup +# 2020-06-08: changed snmp-scan function +# 2020-11-10: removed perfdata +# added wato option to configure 'ignore disks'. For example on the Smart-1 5150 (Array has 12 Disks, +# but only 6 Disks are in the default hardware +# 2021-04-29: rename from checkpoint_soft_raid in to checkpoint_raid +# 2021-08-10: rewritten for CMK 2.0 +# +# sample snmpwalk (one volume / two disks) +# +# .1.3.6.1.4.1.2620.1.6.7.7.1.1.1.1.0 = INTEGER: 1 +# .1.3.6.1.4.1.2620.1.6.7.7.1.1.2.1.0 = INTEGER: 0 +# .1.3.6.1.4.1.2620.1.6.7.7.1.1.3.1.0 = INTEGER: 2 +# .1.3.6.1.4.1.2620.1.6.7.7.1.1.4.1.0 = INTEGER: 2 +# .1.3.6.1.4.1.2620.1.6.7.7.1.1.5.1.0 = INTEGER: 1952448512 +# .1.3.6.1.4.1.2620.1.6.7.7.1.1.6.1.0 = INTEGER: 0 +# .1.3.6.1.4.1.2620.1.6.7.7.1.1.7.1.0 = INTEGER: 1 +# .1.3.6.1.4.1.2620.1.6.7.7.1.1.8.1.0 = Gauge32: 931 +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.1.1.0 = INTEGER: 1 +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.1.2.0 = INTEGER: 2 +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.2.1.0 = INTEGER: 0 +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.2.2.0 = INTEGER: 0 +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.3.1.0 = INTEGER: 0 +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.3.2.0 = INTEGER: 1 +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.4.1.0 = INTEGER: 0 +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.4.2.0 = INTEGER: 1 +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.5.1.0 = STRING: "ATA " +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.5.2.0 = STRING: "ATA " +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.6.1.0 = STRING: "ST1000NM0033-9ZM" +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.6.2.0 = STRING: "ST1000NM0033-9ZM" +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.7.1.0 = STRING: "SN04" +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.7.2.0 = STRING: "SN04" +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.8.1.0 = INTEGER: 1953525168 +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.8.2.0 = INTEGER: 1953525168 +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.9.1.0 = INTEGER: 0 +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.9.2.0 = INTEGER: 0 +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.10.1.0 = INTEGER: 0 +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.10.2.0 = INTEGER: 0 +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.11.1.0 = INTEGER: 100 +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.11.2.0 = INTEGER: 100 +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.12.1.0 = Gauge32: 931 +# .1.3.6.1.4.1.2620.1.6.7.7.2.1.12.2.0 = Gauge32: 931 +# +# +# sample info +# +# [[[u'1', u'0', u'2', u'2', u'1952448512', u'0', u'1', u'931']], +# [[u'1', u'0', u'0', u'0', u'ATA ', u'ST1000NM0033-9ZM', u'SN04', u'1953525168', u'0', u'0', u'100', u'931'], +# [u'2', u'0', u'1', u'1', u'ATA ', u'ST1000NM0033-9ZM', u'SN04', u'1953525168', u'0', u'0', u'100', u'931']]] +# +# + +from dataclasses import dataclass +from typing import List, Dict + +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, + equals, + Result, + State, + SNMPTree, + startswith, + all_of, + any_of, +) + + +@dataclass +class RaidDisk: + diskindex: int + diskvolumeid: str + diskid: int + diskvendor: str + diskproductid: str + diskrevision: str + diskmaxlba: int + diskstate: int + diskflags: str + disksyncstate: int + disksize: int + + +@dataclass +class RaidVolume: + volumeindex: int + volumetype: str + numofdisksonraid: int + volumemaxlba: int + volumestate: int + volumeflags: str + volumesize: int + disks: List[RaidDisk] + + +_raid_volume_type = { + 0: 'RAID-0', + 1: 'RAID-1E', + 2: 'RAID-1', + 3: 'RAID_10', + 4: 'RAID-4', + 5: 'RAID-5', + 6: 'RAID-6', + 7: 'RAID-60', + 8: 'RAID-50', +} + + +_raid_volume_state = { + 0: 'OPTIMAL', + 1: 'DEGRADED', + 2: 'FAILED', +} + +_raid_volume_flags = { + 0: 'NONE', + 1: 'ENABLED', + 2: 'QUIESCED', + 3: 'RESYNC IN PROGRESS', + 4: 'VOLUME INACTIVE', + 5: 'NOT CONFIGURED', + 6: 'USING INTERIM RECOVERY MODE', + 7: 'READY FOR RECOVERY OPERATION', + 8: 'WRONG PHYSICAL DRIVE WAS REPLACED', + 9: 'A PHYSICAL DRIVE IS NOT PROPERLY CONNECTED', + 10: 'HARDWARE IS OVER HEATING', + 11: 'HARDWARE WAS OVERHEATED', + 12: 'CURRENTLY EXPENDING', + 13: 'NOT YET AVAILABLE', + 14: 'QUEUED FOR EXPENSION', + 15: 'MIGRATING', + 16: 'IMPACTED', + 17: 'OFFLINE', + 18: 'CLEARING', +} + +_raid_disk_state = { + 0: 'ONLINE', + 1: 'MISSING', + 2: 'NOT COMPATIBLE', + 3: 'DISC FAILED', + 4: 'INITIALIZING', + 5: 'OFFLINE REQUESTED', + 6: 'FAILED REQUESTED', + 7: 'UNCONFIGURED GOOD SPUN UP', + 8: 'UNCONFIGURED GOOD SPUN DOWN', + 9: 'UNCONFIGURED BAD', + 10: 'HOTSPARE', + 11: 'DRIVE OFFLINE', + 12: 'REBUILD', + 13: 'FAILED', + 14: 'COPYBACK', + 255: 'OTHER OFFLINE', +} + +_raid_disk_flags = { + 0: 'NONE', + 1: 'OUT OF SYNC', + 2: 'QUIESCED', + 3: 'VERIFYING', + 4: 'READY', +} + + +def _get_flags(flags, flags_description): + description = '' + flags = int(flags) + if flags == 0: + return flags_description.get(0) + else: + for x in range(0, len(flags_description)): + if flags & (1 << x) != 0: + if flags_description.get(x + 1) is not None: + description += f'{flags_description.get(x + 1)},' + return description + + +def parse_checkpoint_raid(string_table: List[StringTable]) -> Dict[str, RaidVolume]: + + raw_volumes, raw_disks = string_table + volumes = {} + disks = [] + + for disk in raw_disks: + diskindex, diskvolumeid, diskid, disknumber, diskvendor, diskproductid, diskrevision, diskmaxlba, diskstate, \ + diskflags, disksyncstate, disksize = disk + + if diskindex.isdigit(): + disks.append(RaidDisk( + diskindex=int(diskindex), + diskvolumeid=diskvolumeid, + diskid=int(disknumber), + diskvendor=diskvendor, + diskproductid=diskproductid, + diskrevision=diskrevision, + diskmaxlba=int(diskmaxlba), + diskstate=int(diskstate), + diskflags=_get_flags(diskflags, _raid_disk_flags), + disksyncstate=int(disksyncstate), + disksize=int(disksize) + )) + + for volume in raw_volumes: + volumeindex, volumeid, volumetype, numofdisksonraid, volumemaxlba, volumestate, volumeflags, volumesize = volume + + if volumeindex.isdigit(): + volumes[volumeid] = RaidVolume( + volumeindex=int(volumeindex), + volumetype=_raid_volume_type.get(int(volumetype), f'unknown ({volumetype}'), + numofdisksonraid=int(numofdisksonraid), + volumemaxlba=int(volumemaxlba), + volumestate=int(volumestate), + volumeflags=_get_flags(volumeflags, _raid_volume_flags), + volumesize=int(volumesize), + disks=[] + ) + for disk in disks: + if disk.diskvolumeid == volumeid: + volumes[volumeid].disks.append(disk) + + return volumes + + +def discovery_checkpoint_raid(section: Dict[str, RaidVolume]) -> DiscoveryResult: + for key in section.keys(): + yield Service(item=key) + + +def check_checkpoint_raid(item, params, section: Dict[str, RaidVolume]) -> CheckResult: + ignore_disks = params['ignore_disks'].split(',') + + try: + volume = section[item] + except KeyError: + yield Result(state=State.CRIT, summary='Volume not found in SNMP Data') + return + + # volume infos + + details = '' + details += f'Volume Type: {volume.volumetype}, ' + details += f'# of Disks: {volume.numofdisksonraid}, ' + details += f'Size: {volume.volumesize}GB, ' + details += f'max LBA: {volume.volumemaxlba}, ' + details += f'Flags: {volume.volumeflags}' + + summary = '' + summary += f'{volume.volumetype}, ' + summary += f'{volume.numofdisksonraid} disks, ' + summary += f'{volume.volumesize}GB, ' + summary += f'State: {_raid_volume_state.get(volume.volumestate)}' + + if volume.volumestate != 0: + yield Result(state=State.CRIT, summary=summary, details=details) + else: + yield Result(state=State.OK, summary=summary, details=details) + + # disk infos + disks = volume.disks + disks_in_array = 0 + disks_online = 0 + disks_ignored = 0 + for disk in disks: + diskid = disk.diskid + if not str(diskid) in ignore_disks: + disks_in_array += 1 + syncstate = disk.disksyncstate + details = '' + details += f'Disk ID: {diskid}, ' + details += f'State: {_raid_disk_state.get(disk.diskstate)}, ' + details += f'Sync: {syncstate}%, ' + details += f'Size: {disk.disksize}GB, ' + if disk.diskmaxlba > 0: + details += f'max LBA: {disk.diskmaxlba}, ' + if disk.diskvendor != 'UNKNOWN': + details += f'Vendor: {disk.diskvendor}, ' + details += f'Product ID: {disk.diskproductid}, ' + if disk.diskrevision != '': + details += f'Revision: {disk.diskrevision}, ' + details += f'Flags: {disk.diskflags}' + if disk.diskstate != 0: + yield Result(state=State.CRIT, + notice=f'Disk ID: {diskid}, state: {_raid_disk_state.get(disk.diskstate)}', + details=details) + else: + yield Result(state=State.OK, notice=details, + ) + disks_online += 1 + else: + disks_ignored += 1 + + if disks_in_array == disks_online: + yield Result(state=State.OK, summary=f'{disks_online}/{disks_in_array} Disks online') + else: + yield Result(state=State.CRIT, summary=f'{disks_online}/{disks_in_array} Disks online') + + if disks_ignored > 0: + yield Result(state=State.OK, summary=f'{disks_ignored} Disks ignored') + + +register.snmp_section( + name='checkpoint_raid', + parse_function=parse_checkpoint_raid, + fetch=[ + SNMPTree( + base='.1.3.6.1.4.1.2620.1.6.7.7.1.1', # CHECKPOINT-MIB::raidVolumeEntry + oids=[ + '1', # raidVolumeIndex + '2', # raidVolumeID + '3', # raidVolumeType + '4', # numOfDisksOnRaid + '5', # raidVolumeMaxLBA + '6', # raidVolumeState + '7', # raidVolumeFlags + '8', # raidVolumeSize + ] + ), + SNMPTree( + base='.1.3.6.1.4.1.2620.1.6.7.7.2.1', # CHECKPOINT-MIB::fwLSConnEntry + oids=[ + '1', # raidDiskIndex + '2', # raidDiskVolumeID + '3', # raidDiskID + '4', # raidDiskNumber + '5', # raidDiskVendor + '6', # raidDiskProductID + '7', # raidDiskRevision + '8', # raidDiskMaxLBA + '9', # raidDiskState + '10', # raidDiskFlags + '11', # raidDiskSyncState + '12', # raidDiskSize + ] + ) + + ], + detect=any_of( + startswith('.1.3.6.1.2.1.1.2.0', '.1.3.6.1.4.1.2620'), + all_of( + equals('.1.3.6.1.2.1.1.2.0', '.1.3.6.1.4.1.8072.3.2.10'), + equals('.1.3.6.1.4.1.2620.1.6.1.0', 'SVN Foundation'), + ) + ) +) + +register.check_plugin( + name='checkpoint_raid', + service_name='RAID ID %s', + discovery_function=discovery_checkpoint_raid, + check_function=check_checkpoint_raid, + check_default_parameters={ + 'ignore_disks': '' + }, + check_ruleset_name='checkpoint_raid', +) diff --git a/checkpoint_raid.mkp b/checkpoint_raid.mkp index 01cca1612a5ce8734bbf029ecd33a3b1e00e4ca8..6cca976e0c1b91dc244f8d685ac01c7dbc962fa7 100644 Binary files a/checkpoint_raid.mkp and b/checkpoint_raid.mkp differ diff --git a/packages/checkpoint_raid b/packages/checkpoint_raid index cb964e8b22a726014427303ddc3638de9e3b1764..276d5db7d27100db123a177c27bf29c11d10cb76 100644 --- a/packages/checkpoint_raid +++ b/packages/checkpoint_raid @@ -1,14 +1,14 @@ -{'author': u'Th.L. (thl-cmk[at]outlook[dot]com)', - 'description': u'monitor Check Point appliance RAID\n', - 'download_url': 'http://thl-cmk.hopto.org/', - 'files': {'checkman': ['checkpoint_raid'], - 'checks': ['checkpoint_raid'], +{'author': 'Th.L. (thl-cmk[at]outlook[dot]com)', + 'description': 'monitor Check Point appliance RAID arrays\n', + 'download_url': 'https://thl-cmk.hopto.org', + 'files': {'agent_based': ['checkpoint_raid.py'], + 'checkman': ['checkpoint_raid'], 'web': ['plugins/metrics/checkpoint_raid.py', 'plugins/wato/checkpoint_raid.py']}, 'name': 'checkpoint_raid', 'num_files': 4, - 'title': u'Check Point appliance RAID', - 'version': '20210429.v0.3', - 'version.min_required': '1.2.8b8', - 'version.packaged': '1.6.0p12', + 'title': 'Check Point appliance RAID', + 'version': '20210810.v0.4', + 'version.min_required': '2.0.0', + 'version.packaged': '2021.07.14', 'version.usable_until': None} \ No newline at end of file diff --git a/web/plugins/wato/checkpoint_raid.py b/web/plugins/wato/checkpoint_raid.py index 1f6ba24ef8a45019f0569020da0b991295b757ab..a5cbe15160770e7182d40deda148fa7d40329c5b 100644 --- a/web/plugins/wato/checkpoint_raid.py +++ b/web/plugins/wato/checkpoint_raid.py @@ -6,28 +6,43 @@ # Author: thl-cmk[at]outlook[dot]com # URL : https://thl-cmk.hopto.org # -# Check_MK checkpoint_soft_rais WATO plugin -# -# +# Check_MK checkpoint_raid WATO plugin # +from cmk.gui.i18n import _ +from cmk.gui.valuespec import ( + Dictionary, + TextAscii, +) + +from cmk.gui.plugins.wato import ( + CheckParameterRulespecWithItem, + rulespec_registry, + RulespecGroupCheckParametersNetworking, +) -register_check_parameters( - subgroup_networking, - 'checkpoint_raid', - _('Check Point RAID'), - Dictionary( + +def _parameter_valuespec_checkpoint_raid(): + return Dictionary( help=_(''), elements=[ ('ignore_disks', TextUnicode( title=_('list of comma separated disk IDs to ignore'), - help=_('This is usefull for RAID Arrays with missing disks like on the Smart-1 5150. There the ' + help=_('This is useful for RAID Arrays with missing disks, like on the Smart-1 5150. There the ' 'array is configured for 12 Disk but in the default Hardware are only 6 Disks.'), default_value=None, allow_empty=False, )), ], - ), - None, - match_type='dict', -) + ) + + +rulespec_registry.register( + CheckParameterRulespecWithItem( + check_group_name='checkpoint_raid', + group=RulespecGroupCheckParametersNetworking, + item_spec=lambda: TextAscii(title=_('RAID ID'), ), + match_type='dict', + parameter_valuespec=_parameter_valuespec_checkpoint_raid, + title=lambda: _('Check Point RAID'), + ))