From c3a4c18975d8f90725d884c5f79cb82e7f955988 Mon Sep 17 00:00:00 2001 From: "th.l" <thl-cmk@outlook.com> Date: Sat, 31 Jul 2021 16:38:04 +0200 Subject: [PATCH] update project --- agent_based/cisco_flash.py | 256 +++++++++++++++++++ agent_based/inv_cisco_flash.py | 352 +++++++++++++++++++++++++++ cisco_flash.mkp | Bin 6794 -> 8138 bytes packages/cisco_flash | 24 +- web/plugins/metrics/cisco_flash.py | 93 ++++--- web/plugins/views/inv_cisco_flash.py | 83 +++---- web/plugins/wato/cisco_flash.py | 81 +++--- web/plugins/wato/inv_cisco_flash.py | 199 +++++++-------- 8 files changed, 856 insertions(+), 232 deletions(-) create mode 100644 agent_based/cisco_flash.py create mode 100644 agent_based/inv_cisco_flash.py diff --git a/agent_based/cisco_flash.py b/agent_based/cisco_flash.py new file mode 100644 index 0000000..9f6e0eb --- /dev/null +++ b/agent_based/cisco_flash.py @@ -0,0 +1,256 @@ +#!/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 : 2019-10-28 +# +# Monitor status of Cisco flash file systems +# +# 2019-10-28: initial release +# 2019-11-04: added wato for cisco_flash +# 2021-07-31: rewritten for CMK 2.0 +# +# snmpwalk sample +# +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.2.1.1 = INTEGER: 1 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.2.2.1 = INTEGER: 1 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.3.1.1 = INTEGER: 1 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.3.2.1 = INTEGER: 1 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.4.1.1 = Gauge32: 2908606464 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.4.2.1 = Gauge32: 4294967295 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.5.1.1 = Gauge32: 1756741632 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.5.2.1 = Gauge32: 4294967295 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.6.1.1 = Gauge32: 624 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.6.2.1 = Gauge32: 0 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.7.1.1 = INTEGER: 3 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.7.2.1 = INTEGER: 3 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.8.1.1 = INTEGER: 3 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.8.2.1 = INTEGER: 3 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.9.1.1 = INTEGER: 3 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.9.2.1 = INTEGER: 3 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.10.1.1 = STRING: "bootflash" +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.10.2.1 = STRING: "usb0" +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.11.1.1 = INTEGER: 2 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.11.2.1 = INTEGER: 2 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.12.1.1 = INTEGER: 63 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.12.2.1 = INTEGER: 63 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.13.1.1 = Counter64: 2908606464 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.13.2.1 = Counter64: 16424566784 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.14.1.1 = Counter64: 1756741632 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.14.2.1 = Counter64: 16424558592 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.15.1.1 = INTEGER: 10 +# .1.3.6.1.4.1.9.9.10.1.1.4.1.1.15.2.1 = INTEGER: 10 +# +# sample info +# +# [ +# [u'1.1', u'2908606464', u'1758527488', u'446', u'3', u'bootflash', u'2', u'2908606464', u'1758527488', u'10'], +# [u'2.1', u'4294967295', u'4294967295', u'0', u'3', u'usb0', u'2', u'16423845888', u'7506280448', u'10'] +# ] +# +# + +from dataclasses import dataclass +from typing import Optional, 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, + SNMPTree, + contains, + check_levels, + OIDEnd, + Result, + render, + State, + Metric, + all_of, + exists, +) + + +@dataclass +class Partition: + size: Optional[int] = None + freespace: Optional[int] = None + usedspace: Optional[int] = None + index: Optional[int] = None + flashindex: Optional[int] = None + neederasure: Optional[bool] = None + lowspacenotifythreshold: Optional[int] = None + filecount: Optional[int] = None + + +_partneederasure = { + '1': True, + '2': False, +} + + +########################################################################### +# +# DATA Parser function +# +########################################################################### + + +def parse_cisco_flash(string_table: StringTable) -> Dict[str, Partition]: + partitions: Dict[str, Partition] = {} + + for cflPartition in string_table: + cflpartOID_END, cflpartSize, cflpartFreeSpace, cflpartFileCount, cflpartName, cflpartNeedErasure, \ + cflpartSizeExtended, cflpartFreeSpaceExtended, cflpartLowSpaceNotifThreshold = cflPartition + + cflDevIndex, cflPartIndex = cflpartOID_END.split('.') + + cflpartFreeSpace = int(cflpartFreeSpace) if cflpartFreeSpace.isdigit() else None + cflpartFreeSpaceExtended = int(cflpartFreeSpaceExtended) if cflpartFreeSpaceExtended.isdigit() else None + cflpartSize = int(cflpartSize) if cflpartSize.isdigit() else None + cflpartSizeExtended = int(cflpartSizeExtended) if cflpartSizeExtended.isdigit() else None + + freespace = cflpartFreeSpaceExtended if cflpartFreeSpaceExtended else cflpartFreeSpace + size = cflpartSizeExtended if cflpartSizeExtended else cflpartSize + + if size > 0: + partitions.update({ + cflpartName: Partition( + index=int(cflPartIndex) if cflPartIndex.isdigit() else None, + flashindex=int(cflDevIndex) if cflDevIndex.isdigit() else None, + size=size, + freespace=freespace, + usedspace=size - freespace, + filecount=int(cflpartFileCount) if cflpartFileCount.isdigit() else None, + neederasure=_partneederasure.get(cflpartNeedErasure, None), + lowspacenotifythreshold=int( + cflpartLowSpaceNotifThreshold) if cflpartLowSpaceNotifThreshold.isdigit() else None + )}) + + return partitions + + +########################################################################### +# +# INVENTORY function +# +########################################################################### + + +def discovery_cisco_flash(section: Dict[str, Partition]) -> DiscoveryResult: + for key in section: + yield Service(item=key) + + +########################################################################### +# +# CHECK function +# +########################################################################### + + +def check_cisco_flash(item, params, section: Dict[str, Partition]) -> CheckResult: + try: + partition = section[item] + except KeyError: + return + + if partition.lowspacenotifythreshold: + spacewarn = partition.size - ((partition.size / 100) * partition.lowspacenotifythreshold) + else: + spacewarn = partition.size + + yield from check_levels( + value=partition.usedspace, + label='Space used', + metric_name='cisco_flash_partusedspace', + render_func=render.disksize, + levels_upper=(spacewarn, partition.size - 10), + boundaries=(0, partition.size), + ) + + percentused = 100 * partition.usedspace / partition.size + + yield from check_levels( + value=percentused, + label='Percent', + levels_upper=params['levels_upper_percent'], + render_func=render.percent, + boundaries=(0, 100), + metric_name='cisco_flash_percent_used' + ) + + if partition.neederasure: + yield Result(state=State.WARN, notice='Partition needs erasure') + + yield Metric( + value=partition.filecount, + name='cisco_flash_partfilecount', + boundaries=(0, None) + ) + + for key, value in [ + ('Device index', partition.flashindex), + ('Partition index', partition.index), + ('Partition need erasure', partition.neederasure), + ('Partition low space notify threshold', partition.lowspacenotifythreshold), + ]: + if value: + yield Result(state=State.OK, notice=f'{key}: {value}') + + size = max(partition.size, 1) # for size = 0 --> div/0 + size = size / 1000 / 1000 + usedspace = partition.usedspace / 1000 / 1000 + freespace = partition.freespace / 1000 / 1000 + + yield Result(state=State.OK, notice=f'Used: {usedspace:.2f} of {size:.2f} MB, {freespace:.2f} MB free') + + if partition.filecount: + yield Result(state=State.OK, summary=f'File count: {partition.filecount}') + + +########################################################################### +# +# SNMP section +# +########################################################################### + + +register.snmp_section( + name='cisco_flash', + parse_function=parse_cisco_flash, + fetch=SNMPTree( + base='.1.3.6.1.4.1.9.9.10.1.1.4.1.1', # + oids=[ + OIDEnd(), + '4', # ciscoFlashPartitionSize + '5', # ciscoFlashPartitionFreeSpace + '6', # ciscoFlashPartitionFileCount + '10', # ciscoFlashPartitionName + '11', # ciscoFlashPartitionNeedErasure + '13', # ciscoFlashPartitionSizeExtended + '14', # ciscoFlashPartitionFreeSpaceExtended + '15', # ciscoFlashPartitionLowSpaceNotifThreshold + ] + ), + detect=all_of( + contains('.1.3.6.1.2.1.1.1.0', 'cisco'), # sysDescr + exists('.1.3.6.1.4.1.9.9.10.1.1.4.1.1.10.*'), # CISCO-FLASH-MIB::ciscoFlashDevicesSupported + )) + +register.check_plugin( + name='cisco_flash', + service_name='Flash partition %s', + discovery_function=discovery_cisco_flash, + check_function=check_cisco_flash, + check_default_parameters={ + 'levels_upper_percent': (85, 90), + }, + check_ruleset_name='cisco_flash', +) diff --git a/agent_based/inv_cisco_flash.py b/agent_based/inv_cisco_flash.py new file mode 100644 index 0000000..2a23f76 --- /dev/null +++ b/agent_based/inv_cisco_flash.py @@ -0,0 +1,352 @@ +#!/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 : 2019-10-22 +# +# invetory of cisco flash devices +# +# 2019-10-22 : initial release +# 2021-07-31: rewritten for CMK 2.0 +# +# + +from typing import List, Dict, Any +from dataclasses import dataclass +from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import ( + StringTable, + InventoryResult, +) + +from cmk.base.plugins.agent_based.agent_based_api.v1 import ( + OIDEnd, +) + +from cmk.base.plugins.agent_based.agent_based_api.v1 import ( + register, + SNMPTree, + TableRow, + exists, + all_of, + contains, +) + + +@dataclass +class Section: + flash_devices: List[Dict[str, Any]] + flash_chips: List[Dict[str, Any]] + flash_partitions: List[Dict[str, Any]] + + +_DevProgrammingJumper = { + '1': 'installed', + '2': 'not installed', + '3': 'unknown', +} + +_DevRemovable = { + '1': True, + '2': False, +} + +_PartStatus = { + '1': 'Read Only', + '2': 'run from flash', + '3': 'Read Write', +} + +_PartChecksumAlgorithm = { + '1': 'simple Checksum', + '2': 'undefined', + '3': 'simple CRC', +} + +_PartUpgradeMethod = { + '1': 'unkown', + '2': 'rxboot Flash Load Helper', + '3': 'direct', +} + +_PartNeedErasure = { + '1': True, + '2': False, +} + + +def parse_inv_cisco_flash(string_table: List[StringTable]) -> Section: + cflDevices, cflChips, cflPartitions = string_table + + flash_devices = [] + for cflDevice in cflDevices: + clfOID_END, cflDevSize, cflDevMinPartitionSize, cflDevMaxPartitions, cflDevPartitions, cflDevChipCount, \ + cflDevName, cflDevDescr, cflDevController, cflDevProgrammingJumper, cflDevInitTime, cflDevRemovable, \ + cflDevPhyEntIndex, cflDevNameExtended, cflDevSizeExtended, cflDevMinPartitionSizeExtended = cflDevice + + cflDevSize = int(cflDevSize) if cflDevSize.isdigit() else None + cflDevSizeExtended = int(cflDevSizeExtended) if cflDevSizeExtended.isdigit() else None + cflDevMinPartitionSize = int(cflDevMinPartitionSize) if cflDevMinPartitionSize.isdigit() else None + cflDevMinPartitionSizeExtended = int( + cflDevMinPartitionSizeExtended) if cflDevMinPartitionSizeExtended.isdigit() else None + + # change sizes to MB + size = cflDevSizeExtended if cflDevSizeExtended else cflDevSize + size = size / 1024 / 1024 if size else None + + minpartitionsize = cflDevMinPartitionSizeExtended if cflDevMinPartitionSizeExtended else cflDevMinPartitionSize + minpartitionsize = minpartitionsize / 1024 / 1024 if minpartitionsize else None + + flash_device = {} + for key, value in [ + ('index', clfOID_END), + ('name', cflDevName), + ('description', cflDevDescr), + ('controller', cflDevController), + ('programmingjumper', _DevProgrammingJumper.get(cflDevProgrammingJumper)), + ('inittime', cflDevInitTime), + ('removable', _DevRemovable.get(cflDevRemovable)), + ('nameextended', cflDevNameExtended), + ('physentindex', cflDevPhyEntIndex), + ('size', size), + ('minpartitionsize', minpartitionsize), + ('maxprtitions', cflDevMaxPartitions), + ('partitions', cflDevPartitions), + ('chipcount', cflDevChipCount), + ]: + if value: + flash_device.update({key: value}) + flash_devices.append(flash_device) + + flash_chips = [] + + for cflChip in cflChips: + cflOID_END, cflChipCode, cflChipDescr, cflChipWriteRetries, cflChipEraseRetries, cflChipMaxWriteRetries, \ + cflChipMaxEraseRetries = cflChip + + cflDevIndex, cflChipIndex = cflOID_END.split('.') + + flash_chip = {} + for key, value in [ + ('index', cflChipIndex), + ('flashindex', cflDevIndex), + ('code', cflChipCode), + ('description', cflChipDescr), + ('writeretries', cflChipWriteRetries), + ('eraseretries', cflChipEraseRetries), + ('maxwriteretries', cflChipMaxWriteRetries), + ('maxeraseretries', cflChipMaxEraseRetries), + ]: + if value: + flash_chip.update({key: value}) + + flash_chips.append(flash_chip) + + flash_partitions = [] + for cflPartition in cflPartitions: + cflpartOID_END, cflpartStartChip, cflpartEndChip, cflpartSize, cflpartFreeSpace, cflpartFileCount, \ + cflpartChecksumAlgorithm, cflpartStatus, cflpartUpgradeMethod, cflpartName, cflpartNeedErasure, \ + cflpartFileNameLength, cflpartSizeExtended, cflpartFreeSpaceExtended, \ + cflpartLowSpaceNotifThreshold = cflPartition + + cflDevIndex, cflPartIndex = cflpartOID_END.split('.') + + cflpartSize = int(cflpartSize) if cflpartSize.isdigit() else None + cflpartFreeSpace = int(cflpartFreeSpace) if cflpartFreeSpace.isdigit() else None + cflpartSizeExtended = int(cflpartSizeExtended) if cflpartSizeExtended.isdigit() else None + cflpartFreeSpaceExtended = int(cflpartFreeSpaceExtended) if cflpartFreeSpaceExtended.isdigit() else None + + # change sizes to MB + size = cflpartSizeExtended if cflpartSizeExtended else cflpartSize + size = size / 1024 / 1024 if size else None + + freespace = cflpartFreeSpaceExtended if cflpartFreeSpaceExtended else cflpartFreeSpace + freespace = freespace / 1024 / 1024 if freespace else None + + flash_partition = {} + for key, value in [ + ('index', cflPartIndex), + ('flashindex', cflDevIndex), + ('startchip', cflpartStartChip), + ('endchip', cflpartEndChip), + ('filecount', cflpartFileCount), + ('crcsumalgo', _PartChecksumAlgorithm.get(cflpartChecksumAlgorithm)), + ('status', _PartStatus.get(cflpartStatus)), + ('upgrademethod', _PartUpgradeMethod.get(cflpartUpgradeMethod)), + ('name', cflpartName), + ('neederasure', _PartNeedErasure.get(cflpartNeedErasure)), + ('filenamelength', cflpartFileNameLength), + ('lowspacenotifythreshold', cflpartLowSpaceNotifThreshold), + ('size', size), + ('freespace', freespace), + ]: + if value: + flash_partition.update({key: value}) + + flash_partitions.append(flash_partition) + + return Section( + flash_devices=flash_devices, + flash_chips=flash_chips, + flash_partitions=flash_partitions + ) + + +def inventory_cisco_flash(params, section: Section) -> InventoryResult: + flash_devices = section.flash_devices + flash_chips = section.flash_chips + flash_partitions = section.flash_partitions + + # inventory of cisco flash devices + removecolumns = [] + disabled = False + # + if params: + disabled = params.get('cflDeviceDisable', disabled) + # get list of columns to remove from inventory + removecolumns = params.get('cflDeviceRemovecolumns', removecolumns) + + if not disabled: + + path = ['hardware', 'components', 'flash', 'devices'] + + for flash_device in flash_devices: + key_columns = {'index': flash_device['index'], } + flash_device.pop('index') + for key in flash_device.keys(): + if key in removecolumns: + flash_device.pop(key) + + yield TableRow( + path=path, + key_columns=key_columns, + inventory_columns=flash_device + ) + + # inventory of cisco flash chips + + removecolumns = [] + disabled = False + + if params: + disabled = params.get('cflChipDisable', disabled) + # get list of columns to remove from inventory + removecolumns = params.get('cflChipRemovecolumns', removecolumns) + + if not disabled: + path = ['hardware', 'components', 'flash', 'chips'] + + for flash_chip in flash_chips: + key_columns = {'index': flash_chip['index'], 'flashindex': flash_chip['flashindex']} + flash_chip.pop('index') + flash_chip.pop('flashindex') + for key in flash_chip.keys(): + if key in removecolumns: + flash_chip.pop(key) + + yield TableRow( + path=path, + key_columns=key_columns, + inventory_columns=flash_chip + ) + + # inventory of cisco flash partitions + + removecolumns = [] + disabled = False + + if params: + disabled = params.get('cflPartitionDisable', disabled) + # get list of columns to remove from inventory + removecolumns = params.get('cflPartitionRemovecolumns', removecolumns) + + if not disabled: + path = ['hardware', 'components', 'flash', 'partitions'] + + for flash_partition in flash_partitions: + key_columns = {'index': flash_partition['index'], 'flashindex': flash_partition['flashindex']} + flash_partition.pop('index') + flash_partition.pop('flashindex') + for key in flash_partition.keys(): + if key in removecolumns: + flash_partition.pop(key) + + yield TableRow( + path=path, + key_columns=key_columns, + inventory_columns=flash_partition + ) + + +register.snmp_section( + name='inv_cisco_flash', + parse_function=parse_inv_cisco_flash, + fetch=[ + SNMPTree( + base='.1.3.6.1.4.1.9.9.10.1.1.2.1', # + oids=[ + OIDEnd(), # ifIndex + '2', # ciscoFlashDeviceSize + '3', # ciscoFlashDeviceMinPartitionSize + '4', # ciscoFlashDeviceMaxPartitions + '5', # ciscoFlashDevicePartitions + '6', # ciscoFlashDeviceChipCount + '7', # ciscoFlashDeviceName + '8', # ciscoFlashDeviceDescr + '9', # ciscoFlashDeviceController + '11', # ciscoFlashDeviceProgrammingJumper + '12', # ciscoFlashDeviceInitTime + '13', # ciscoFlashDeviceRemovable + '14', # ciscoFlashPhyEntIndex + '15', # ciscoFlashDeviceNameExtended + '16', # ciscoFlashDeviceSizeExtended + '17', # ciscoFlashDeviceMinPartitionSizeExtended + ] + ), + SNMPTree( + base='.1.3.6.1.4.1.9.9.10.1.1.3.1.1', # + oids=[ + OIDEnd(), # ifIndex + '2', # ciscoFlashChipCode + '3', # ciscoFlashChipDescr + '4', # ciscoFlashChipWriteRetries + '5', # ciscoFlashChipEraseRetries + '6', # ciscoFlashChipMaxWriteRetries + '7', # ciscoFlashChipMaxEraseRetries + ] + ), + SNMPTree( + base='.1.3.6.1.4.1.9.9.10.1.1.4.1.1', # + oids=[ + OIDEnd(), # ifIndex + '2', # ciscoFlashPartitionStartChip + '3', # ciscoFlashPartitionEndChip + '4', # ciscoFlashPartitionSize + '5', # ciscoFlashPartitionFreeSpace + '6', # ciscoFlashPartitionFileCount + '7', # ciscoFlashPartitionChecksumAlgorithm + '8', # ciscoFlashPartitionStatus + '9', # ciscoFlashPartitionUpgradeMethod + '10', # ciscoFlashPartitionName + '11', # ciscoFlashPartitionNeedErasure + '12', # ciscoFlashPartitionFileNameLength + '13', # ciscoFlashPartitionSizeExtended + '14', # ciscoFlashPartitionFreeSpaceExtended + '15', # ciscoFlashPartitionLowSpaceNotifThreshold + ] + ), + ], + detect=all_of( + contains('.1.3.6.1.2.1.1.1.0', 'cisco'), + exists('.1.3.6.1.4.1.9.9.10.1.1.1.*'), # CISCO-FLASH-MIB::ciscoFlashDevicesSupported + ) +) + +register.inventory_plugin( + name='inv_cisco_flash', + inventory_function=inventory_cisco_flash, + inventory_default_parameters={}, + inventory_ruleset_name='inv_cisco_flash', +) diff --git a/cisco_flash.mkp b/cisco_flash.mkp index 03216bd3d176ddaac49f9f0f7a47d8f3cefc7450..21fecb02981367afda43d078598a16f17dda2ee0 100644 GIT binary patch literal 8138 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 QlJm|==8>WYMF<V`KZX(s?f?J) literal 6794 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 diff --git a/packages/cisco_flash b/packages/cisco_flash index 4389a03..6b51347 100644 --- a/packages/cisco_flash +++ b/packages/cisco_flash @@ -1,15 +1,21 @@ -{'author': u'Th.L. (thl-cmk[at]outlook[dot]com)', - 'description': u'Cisco flash plugins:\n - snmp_cisco_flash: inventory plugin for Cisco flash memory\n - cisco_flash: check for Cisco partitions\n\n- 2020-05-13: added support for CMK1.6x\n', +{'author': 'Th.L. (thl-cmk[at]outlook[dot]com)', + 'description': 'Cisco flash plugins:\n' + ' - inv_cisco_flash: inventory plugin for Cisco flash memory\n' + ' - cisco_flash: check for Cisco partitions\n' + '\n' + '- 2020-05-13: added support for CMK1.6x\n' + '- 2021-07-31: rewritten for CMK 2.0\n', 'download_url': 'https://thl-cmk.hopto.org', - 'files': {'checks': ['cisco_flash'], - 'inventory': ['snmp_cisco_flash'], + 'files': {'agent_based': ['cisco_flash.py', 'inv_cisco_flash.py'], + 'checkman': ['cisco_flash'], 'web': ['plugins/metrics/cisco_flash.py', 'plugins/views/inv_cisco_flash.py', 'plugins/wato/cisco_flash.py', 'plugins/wato/inv_cisco_flash.py']}, 'name': 'cisco_flash', - 'num_files': 6, - 'title': u'Cisco flash plugins', - 'version': '20200513v.0.2c', - 'version.min_required': '1.4.0p35', - 'version.packaged': '1.6.0p8'} \ No newline at end of file + 'num_files': 7, + 'title': 'Cisco flash plugins', + 'version': '20210731v.0.3', + '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/metrics/cisco_flash.py b/web/plugins/metrics/cisco_flash.py index 28a3bbe..2c1805f 100644 --- a/web/plugins/metrics/cisco_flash.py +++ b/web/plugins/metrics/cisco_flash.py @@ -1,56 +1,47 @@ -#!/usr/bin/python -# -*- encoding: utf-8; py-indent-offset: 4 -*- +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- # -# Cisco flash metrics plugin -# -# Author: Th.L. +# License: GNU General Public License v2 + +# Author: thl-cmk[at]outlook[dot]com +# URL : https://thl-cmk.hopto.org # Date : 2019-10-28 # +# Cisco flash metrics plugin # - -##################################################################################################################### -# -# define units for perfdata +# change log +# 2019-10-28: initial release +# 2021-07-31: rewritten for CMK 2.0 # -##################################################################################################################### +from cmk.gui.i18n import _ -##################################################################################################################### -# -# define metrics for perfdata -# -##################################################################################################################### +from cmk.gui.plugins.metrics import ( + metric_info, + graph_info, + perfometer_info + +) metric_info['cisco_flash_partusedspace'] = { 'title': _('Sapce used'), 'unit': 'bytes', + 'color': '36/a', +} + +metric_info['cisco_flash_percent_used'] = { + 'title': _('Percent used'), + 'unit': '%', 'color': '26/a', } + metric_info['cisco_flash_partfilecount'] = { 'title': _('# of files'), 'unit': 'count', 'color': '21/a', } - -###################################################################################################################### -# -# map perfdata to metric, not really necessary but makes sure to use the right metrics -# -###################################################################################################################### - -check_metrics['check_mk-cisco_flash'] = { - 'partusedspace': {'name': 'cisco_flash_partusedspace', 'scale': MB}, - 'partfilecount': {'name': 'cisco_flash_partfilecount', }, -} - -###################################################################################################################### -# -# how to graph perdata -# -###################################################################################################################### - -graph_info.append({ +graph_info['cisco_flash_space_used'] = { 'title': _('Space used'), 'metrics': [ ('cisco_flash_partusedspace', 'area'), @@ -60,32 +51,36 @@ graph_info.append({ ('cisco_flash_partusedspace:warn', _('warn')), ], "range": (0, "cisco_flash_partusedspace:max"), -}) +} + +graph_info['cisco_flash_percent_used'] = { + 'title': _('Percent used'), + 'metrics': [ + ('cisco_flash_percent_used', 'area'), + ], + 'scalars': [ + ('cisco_flash_percent_used:crit', _('crit')), + ('cisco_flash_percent_used:warn', _('warn')), + ], + "range": (0, 100), +} -graph_info.append({ +graph_info['cisco_flash_files_on_partition'] = { 'title': _('# of files on partition'), 'metrics': [ ('cisco_flash_partfilecount', 'area'), ], -}) - -###################################################################################################################### -# -# define perf-o-meter -# -###################################################################################################################### +} perfometer_info.append(('stacked', [ { - 'type': 'logarithmic', - 'metric': 'cisco_flash_partusedspace', - 'half_value': 2592000.0, - 'exponent': 2, + 'type': 'linear', + 'segments': ['cisco_flash_percent_used'], + 'total': 100, }, { 'type': 'linear', - 'segments': ['cisco_flash_partfilecount', - ], + 'segments': ['cisco_flash_partfilecount'], 'total': 100000, } ])) diff --git a/web/plugins/views/inv_cisco_flash.py b/web/plugins/views/inv_cisco_flash.py index 631c20b..2738b89 100644 --- a/web/plugins/views/inv_cisco_flash.py +++ b/web/plugins/views/inv_cisco_flash.py @@ -1,53 +1,53 @@ -#!/usr/bin/python -# -*- encoding: utf-8; py-indent-offset: 4 -*- +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- -try: - from cmk.gui.plugins.views.inventory import ( - declare_invtable_view, - render_inv_dicttable, - ) -except: - pass + +from cmk.gui.plugins.views import ( + inventory_displayhints, ) +from cmk.gui.i18n import _ +from cmk.gui.plugins.views.inventory import declare_invtable_view inventory_displayhints.update({ - '.hardware.components.flash.devices:': {'title': _('Flash devices'), 'render': render_inv_dicttable, - 'keyorder': ['index', 'name', 'description', 'size', ], - 'view': 'invflashdevices_of_host', - }, + '.hardware.components.flash.devices:': { + 'title': _('Flash devices'), + 'keyorder': ['index', 'name', 'description', 'size', ], + 'view': 'invflashdevices_of_host', + }, - '.hardware.components.flash.devices:*.index' : {'title': _('Index'), }, - '.hardware.components.flash.devices:*.size' : {'title': _('Size (MB)'), }, - '.hardware.components.flash.devices:*.minpartitionsize' : {'title': _('min. partition size (MB)'), }, - '.hardware.components.flash.devices:*.maxprtitions' : {'title': _('max. partitions'), }, - '.hardware.components.flash.devices:*.chipcount' : {'title': _('Chip count'), }, - '.hardware.components.flash.devices:*.name' : {'title': _('Name'), }, - '.hardware.components.flash.devices:*.description' : {'title': _('Description'), }, - '.hardware.components.flash.devices:*.controller' : {'title': _('Controller'), }, + '.hardware.components.flash.devices:*.index': {'title': _('Index'), }, + '.hardware.components.flash.devices:*.size': {'title': _('Size (MB)'), }, + '.hardware.components.flash.devices:*.minpartitionsize': {'title': _('min. partition size (MB)'), }, + '.hardware.components.flash.devices:*.maxprtitions': {'title': _('max. partitions'), }, + '.hardware.components.flash.devices:*.chipcount': {'title': _('Chip count'), }, + '.hardware.components.flash.devices:*.name': {'title': _('Name'), }, + '.hardware.components.flash.devices:*.description': {'title': _('Description'), }, + '.hardware.components.flash.devices:*.controller': {'title': _('Controller'), }, '.hardware.components.flash.devices:*.programmingjumper': {'title': _('Programming jumper'), }, - '.hardware.components.flash.devices:*.inittime' : {'title': _('Init time'), }, - '.hardware.components.flash.devices:*.removable' : {'title': _('Removable'), }, - '.hardware.components.flash.devices:*.physentindex' : {'title': _('Phys entity index'), }, - '.hardware.components.flash.devices:*.nameextended' : {'title': _('Name extended'), }, - + '.hardware.components.flash.devices:*.inittime': {'title': _('Init time'), }, + '.hardware.components.flash.devices:*.removable': {'title': _('Removable'), }, + '.hardware.components.flash.devices:*.physentindex': {'title': _('Phys entity index'), }, + '.hardware.components.flash.devices:*.nameextended': {'title': _('Name extended'), }, - '.hardware.components.flash.chips:': {'title': _('Flash chips'), 'render' : render_inv_dicttable, - 'keyorder': ['flashindex', 'index', 'description', ], - 'view': 'invflashchips_of_host', - }, + '.hardware.components.flash.chips:': { + 'title': _('Flash chips'), + 'keyorder': ['flashindex', 'index', 'description', ], + 'view': 'invflashchips_of_host', + }, - '.hardware.components.flash.chips:*.index' : {'title': _('Index'), }, - '.hardware.components.flash.chips:*.flashindex' : {'title': _('Device index'), }, - '.hardware.components.flash.chips:*.code' : {'title': _('Code'), }, - '.hardware.components.flash.chips:*.description' : {'title': _('Description'), }, - '.hardware.components.flash.chips:*.writeretries' : {'title': _('Write retries'), }, - '.hardware.components.flash.chips:*.eraseretries' : {'title': _('Erase retries'), }, + '.hardware.components.flash.chips:*.index': {'title': _('Index'), }, + '.hardware.components.flash.chips:*.flashindex': {'title': _('Device index'), }, + '.hardware.components.flash.chips:*.code': {'title': _('Code'), }, + '.hardware.components.flash.chips:*.description': {'title': _('Description'), }, + '.hardware.components.flash.chips:*.writeretries': {'title': _('Write retries'), }, + '.hardware.components.flash.chips:*.eraseretries': {'title': _('Erase retries'), }, '.hardware.components.flash.chips:*.maxwriteretries': {'title': _('max. write retries'), }, '.hardware.components.flash.chips:*.maxeraseretries': {'title': _('max. erasure retries'), }, - '.hardware.components.flash.partitions:': {'title': _('Flash partitions'), 'render' : render_inv_dicttable, - 'keyorder': ['flashindex', 'index', 'name', 'size', 'freespace', 'filecount', ], - 'view': 'invflashpartitions_of_host', - }, + '.hardware.components.flash.partitions:': { + 'title': _('Flash partitions'), + 'keyorder': ['flashindex', 'index', 'name', 'size', 'freespace', 'filecount', ], + 'view': 'invflashpartitions_of_host', + }, '.hardware.components.flash.partitions:*.index': {'title': _('Index'), }, '.hardware.components.flash.partitions:*.flashindex': {'title': _('Device index'), }, @@ -67,4 +67,5 @@ inventory_displayhints.update({ declare_invtable_view('invflashdevices', '.hardware.components.flash.devices:', _('Flash devices'), _('Flash devices')) declare_invtable_view('invflashchips', '.hardware.components.flash.chips:', _('Flash chips'), _('Flash chips')) -declare_invtable_view('invflashpartitions', '.hardware.components.flash.partitions:', _('Flash partitions'), _('Flash partitions')) +declare_invtable_view('invflashpartitions', '.hardware.components.flash.partitions:', _('Flash partitions'), + _('Flash partitions')) diff --git a/web/plugins/wato/cisco_flash.py b/web/plugins/wato/cisco_flash.py index fa76364..e64ce86 100644 --- a/web/plugins/wato/cisco_flash.py +++ b/web/plugins/wato/cisco_flash.py @@ -1,45 +1,56 @@ -#!/usr/bin/python -# -*- encoding: utf-8; py-indent-offset: 4 -*- - +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- # -# Check_MK cisco_flash WATO plugin +# License: GNU General Public License v2 + +# Author: thl-cmk[at]outlook[dot]com +# URL : https://thl-cmk.hopto.org +# Date : 2019-11-04 # -# Author: Th.L. -# Date: 2019-11-04 # +# Check_MK cisco_flash WATO plugin # +# 2019-11-04: initial release +# 2021-07-31: rewritten for CMK 2.0 # # +from cmk.gui.i18n import _ +from cmk.gui.valuespec import ( + Dictionary, + Integer, + TextAscii, + Tuple, +) + +from cmk.gui.plugins.wato import ( + CheckParameterRulespecWithItem, + rulespec_registry, + RulespecGroupCheckParametersNetworking, +) -register_check_parameters( - subgroup_networking, - 'cisco_flash', - _('Cisco Flash'), - Dictionary( - help=_(''), + +def _parameter_valuespec_cisco_flash(): + return Dictionary( elements=[ - ('warn_level_percent', - Integer( - title=_('Warning if above percentage of used space'), - label=_('Warning if above percentage of used space'), - help=_('Warning if above percentage of used space'), - default_value=85, - allow_empty=False, - minvalue=1, - maxvalue=100 - )), - ('crit_level_percent', - Integer( - title=_('Critical if above percentage of used space'), - label=_('Critical if above percentage of used space'), - help=_('Critical if above percentage of used space'), - default_value=90, - allow_empty=False, - minvalue=1, - maxvalue=100 + ('levels_upper_percent', + Tuple( + title=_('Levels for usage in %'), + elements=[ + Integer(title=_('Warning at'), unit='%', default_value=85, minvalue=0, maxvalue=100), + Integer(title=_('Critical at'), unit='%', default_value=90, minvalue=0, maxvalue=100) + ], + )), ], - ), - TextAscii(title=_('Cisco flash')), - match_type='dict', -) + ) + + +rulespec_registry.register( + CheckParameterRulespecWithItem( + check_group_name='cisco_flash', + group=RulespecGroupCheckParametersNetworking, + item_spec=lambda: TextAscii(title=_('Partition specific configuration'), ), + match_type='dict', + parameter_valuespec=_parameter_valuespec_cisco_flash, + title=lambda: _('Cisco flash'), + )) diff --git a/web/plugins/wato/inv_cisco_flash.py b/web/plugins/wato/inv_cisco_flash.py index c4f9d83..fbb73ba 100644 --- a/web/plugins/wato/inv_cisco_flash.py +++ b/web/plugins/wato/inv_cisco_flash.py @@ -1,14 +1,38 @@ -#!/usr/bin/python -# -*- encoding: utf-8; py-indent-offset: 4 -*- +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# License: GNU General Public License v2 -cflDeviceRemovecolumns = [ -# ('index', 'Index'), -# ('size', 'Size (MB)'), -# ('minpartitionsize', 'min. partition size (MB)'), +# Author: thl-cmk[at]outlook[dot]com +# URL : https://thl-cmk.hopto.org +# Date : 2019-10-28 +# +# Cisco flash metrics plugin +# +# change log +# 2019-10-28: initial release +# 2021-07-31: rewritten for CMK 2.0 + + +from cmk.gui.i18n import _ +from cmk.gui.plugins.wato import ( + HostRulespec, + rulespec_registry, +) +from cmk.gui.valuespec import ( + Dictionary, + FixedValue, + ListChoice, +) + +from cmk.gui.plugins.wato.inventory import ( + RulespecGroupInventory, +) + +_inv_cisco_flash_cflDeviceRemovecolumns = [ + ('minpartitionsize', 'min. partition size (MB)'), ('maxprtitions', 'max. partitions'), ('chipcount', 'Chip count'), -# ('name', 'Name'), -# ('description', 'Description'), ('controller', 'Controller'), ('programmingjumper', 'Programming jumper'), ('inittime', 'Init time'), @@ -16,28 +40,20 @@ cflDeviceRemovecolumns = [ ('physentindex', 'Phys entity index'), ('nameextended', 'Name extended'), ] -cflPartitionRemovecolumns = [ -# ('index', 'Index'), -# ('flashindex', 'Device index'), + +_inv_cisco_flash_cflPartitionRemovecolumns = [ ('startchip', 'Start chip'), ('endchip', 'End chip'), -# ('size', 'Size (MB)'), -# ('freespace', 'Free space (MB)'), -# ('filecount', 'File count'), ('crcsumalgo', 'Checksumm algorithm'), ('status', 'Status'), ('upgrademethod', 'Upgrade method'), -# ('name', 'Name'), ('neederasure', 'Need erasure'), ('filenamelength', 'File name length'), ('lowspacenotifythreshold', 'Low space notify threshold (%)'), ] -cflChipRemovecolumns = [ -# ('index', 'Index'), -# ('flashindex', 'Device index'), +_inv_cisco_flash_cflChipRemovecolumns = [ ('code', 'Code'), -# ('description', 'Description'), ('writeretries', 'Write retries'), ('eraseretries', 'Erase retries'), ('maxwriteretries', 'max. write retries'), @@ -45,83 +61,70 @@ cflChipRemovecolumns = [ ] -register_rule('inventory', 'inv_parameters:inv_cisco_flash', - Dictionary( - title=_('Cisco flash inventory'), - elements=[ - ('cflDeviceDisable', - FixedValue( - True, - title=_('disable Flash device inventory'), - )), - ('cflPartitionDisable', - FixedValue( - True, - title=_('disable Flash partition inventory'), - )), - ('cflChipDisable', - FixedValue( - True, - title=_('disable Flash chips inventory'), - )), - ('cflDeviceRemovecolumns', - ListChoice( - title=_('list of columns to remove from flash devices'), - label=_('list of columns to remove from flash devices'), - help=_('information to remove from inventory'), - choices=cflDeviceRemovecolumns, - default_value=[ - # 'size', - # 'minpartitionsize', - # 'maxprtitions', - 'chipcount', - 'controller', - 'programmingjumper', - 'inittime', - # 'removable', - 'physentindex', - 'nameextended', - ], - )), - ('cflPartitionRemovecolumns', - ListChoice( - title=_('list of columns to remove from flash partitions'), - label=_('list of columns to remove from flash partitions'), - help=_('information to remove from inventory'), - choices=cflPartitionRemovecolumns, - default_value=[ - # 'index', - # 'flashindex', - 'startchip', - 'endchip', - #'size', - #'freespace', - # 'filecount', - # 'crcsumalgo', - # 'status', - # 'upgrademethod', - # 'name', - # 'neederasure', - # 'filenamelength', - # 'lowspacenotifythreshold', +def _valuespec_inv_cisco_flash(): + return Dictionary( + title=_('Cisco flash inventory'), + elements=[ + ('cflDeviceDisable', + FixedValue( + True, + title=_('disable Flash device inventory'), + )), + ('cflPartitionDisable', + FixedValue( + True, + title=_('disable Flash partition inventory'), + )), + ('cflChipDisable', + FixedValue( + True, + title=_('disable Flash chips inventory'), + )), + ('cflDeviceRemovecolumns', + ListChoice( + title=_('list of columns to remove from flash devices'), + help=_('information to remove from inventory'), + choices=_inv_cisco_flash_cflDeviceRemovecolumns, + default_value=[ + 'chipcount', + 'controller', + 'programmingjumper', + 'inittime', + 'physentindex', + 'nameextended', + ], + )), + ('cflPartitionRemovecolumns', + ListChoice( + title=_('list of columns to remove from flash partitions'), + help=_('information to remove from inventory'), + choices=_inv_cisco_flash_cflPartitionRemovecolumns, + default_value=[ + 'startchip', + 'endchip', + ], + )), + ('cflChipRemovecolumns', + ListChoice( + title=_('list of columns to remove from flash chips'), + help=_('information to remove from inventory'), + choices=_inv_cisco_flash_cflChipRemovecolumns, + default_value=[ + 'code', + 'writeretries', + 'eraseretries', + 'maxwriteretries', + 'maxeraseretries' + ], + )), + ], + ) + - ], - )), - ('cflChipRemovecolumns', - ListChoice( - title=_('list of columns to remove from flash chips'), - label=_('list of columns to remove from flash chips'), - help=_('information to remove from inventory'), - choices=cflChipRemovecolumns, - default_value=[ - 'code', - 'writeretries', - 'eraseretries', - 'maxwriteretries', - 'maxeraseretries' - ], - )), - ], - ), - match='dict', - ) +rulespec_registry.register( + HostRulespec( + group=RulespecGroupInventory, + match_type='dict', + name='inv_parameters:inv_cisco_flash', + valuespec=_valuespec_inv_cisco_flash, + )) -- GitLab