Collection of CheckMK checks (see https://checkmk.com/). All checks and plugins are provided as is. Absolutely no warranty. Send any comments to thl-cmk[at]outlook[dot]com

Skip to content
Snippets Groups Projects
Commit 510a2d6a authored by thl-cmk's avatar thl-cmk :flag_na:
Browse files

update project

parent 277603b6
No related branches found
No related tags found
No related merge requests found
[PACKAGE]: ../../raw/master/packagee-0.1.2-20230706.mkp "package-0.1.2-20230706.mkp"
[PACKAGE]: ../../raw/master/mkp/vsphere_topo-0.0.1-20240709.mkp "vsphere_topo-0.0.1-20240709.mkp"
# Title
A short description about the plugin
......
File added
title: Dummy check man page - used as template for new check manuals
agents: linux, windows, aix, solaris, hpux, vms, freebsd, snmp
catalog: see modules/catalog.py for possible values
license: GPL
distribution: check_mk
description:
Describe here: (1) what the check actually does, (2) under which
circumstances it goes warning/critical, (3) which devices are supported
by the check, (4) if the check requires a separated plugin or
tool or separate configuration on the target host.
item:
Describe the syntax and meaning of the check's item here. Provide all
information one needs if coding a manual check with {checks +=} in {main.mk}.
Give an example. If the check uses {None} as sole item,
then leave out this section.
examples:
# Give examples for configuration in {main.mk} here. If the check has
# configuration variable, then give example for them here.
# set default levels to 40 and 60 percent:
foo_default_values = (40, 60)
# another configuration variable here:
inventory_foo_filter = [ "superfoo", "superfoo2" ]
perfdata:
Describe precisely the number and meaning of performance variables
the check sends. If it outputs no performance data, then leave out this
section.
inventory:
Describe how the inventory for the check works. Which items
will it find? Describe the influence of check specific
configuration parameters to the inventory.
[parameters]
foofirst(int): describe the first parameter here (if parameters are grouped
as tuple)
fooother(string): describe another parameter here.
[configuration]
foo_default_levels(int, int): Describe global configuration variable of
foo here. Important: also tell the user how they are preset.
#!/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 : 2024-07-05
# File : vsphere_topo/agent_based/packages.py
from collections.abc import Mapping, Sequence
from time import time_ns
from typing import Any
from cmk.agent_based.v2 import (
CheckPlugin,
CheckResult,
DiscoveryResult,
Result,
Service,
State,
)
from cmk.base.check_api import host_name
from cmk_addons.plugins.vsphere_topo.lib.utils import (
EMBLEM_CLUSTER,
EMBLEM_DATA_CENTER,
EMBLEM_DATA_STORE,
LiveStatusConnection,
OMD_ROOT,
PARAM_ADD_DUMMY_TOPOLOGIES,
PARAM_CLUSTER,
PARAM_DATA_CENTER,
PARAM_DATA_STORE,
PARAM_DATA_STORE_AS_SERVICE,
PARAM_DONT_ADD_VC_AS_VM,
PARAM_HOST_SYSTEMS,
PARAM_MAKE_DEFAULT,
PARAM_VM_NAMES,
RULE_SET_NAME_VSPHERE_TOPO,
adjust_name,
get_emblem,
save_data_to_file,
)
Section = Mapping[str, object]
def discover_vsphere_topo(
section_esx_vsphere_clusters: Section | None,
section_esx_vsphere_virtual_machines: Sequence[Section] | None,
section_esx_vsphere_datastores: Section | None,
) -> DiscoveryResult:
yield Service()
def check_vsphere_topo(
params: Mapping[str, Any],
section_esx_vsphere_clusters: Section | None,
section_esx_vsphere_virtual_machines: Sequence[Section] | None,
section_esx_vsphere_datastores: Section | None,
) -> CheckResult:
def add_host(host: str, emblem: str | None = None, link2core: bool = True):
if objects.get(host) is not None:
return
metadata = {}
if emblem is not None:
metadata = {
'images': {
'emblem': emblem, # node image
},
}
objects[host] = {
"name": host,
"link": {'core': host} if link2core else {},
'metadata': metadata,
}
def add_service(host: str, service: str, emblem: str | None = None, link2core: bool = True):
obj_id = f'{service}@{host}'
if objects.get(obj_id) is not None:
return
metadata = {}
if emblem is not None:
metadata = {
'images': {
'emblem': emblem, # node image
},
}
objects[obj_id] = {
"name": service,
"link": {'core': [host, service]} if link2core else {},
'metadata': metadata,
}
def add_connection(left: str, right: str):
connection = [left, right]
connection.sort()
connections.append([connection, {'line_config': {'css_styles': {'stroke-dasharray': 'unset'}}}])
start_time = time_ns()
objects = {}
connections = []
vsphere_host: str = host_name().strip()
add_host(host=vsphere_host)
__clusters = {
'HA': {
'datacenter': 'datacenter-21',
'hostsystems': 'esxi01, esxi02',
'vms': 'vmname01, vmname02, vmname03'
}
}
if section_esx_vsphere_clusters is None:
yield Result(state=State.UNKNOWN, summary='Found no vSphere data centers/clusters')
return
for cluster in section_esx_vsphere_clusters:
data_center: str = section_esx_vsphere_clusters[cluster]['datacenter'].strip()
if objects.get(data_center) is None:
add_host(
host=data_center,
emblem=get_emblem(EMBLEM_DATA_CENTER, params.get(PARAM_DATA_CENTER)),
link2core=False,
)
add_connection(data_center, vsphere_host)
add_host(
host=cluster,
emblem=get_emblem(EMBLEM_CLUSTER, params.get(PARAM_CLUSTER)),
link2core=False
)
add_connection(cluster, data_center)
host_systems: Sequence[str] = section_esx_vsphere_clusters[cluster].get('hostsystems', '').split(',')
for host_system in host_systems:
esx_name: str = adjust_name(host_system, params.get(PARAM_HOST_SYSTEMS, {}))
add_host(host=esx_name)
add_connection(cluster, esx_name)
__VMs = [
{
'vm_name': 'vmname01',
'hostsystem': 'esxi01',
'powerstate': 'poweredOff',
'guest_os': 'Microsoft Windows Server 2008 R2 (64-bit)',
'compatibility': 'vmx-10',
'uuid': '420354bd-bee0-88d5-053c-d4f424106b0f'
},
{
'vm_name': 'vmname02',
'hostsystem': 'esxi02',
'powerstate': 'poweredOn',
'guest_os': 'Microsoft Windows Server 2019 (64-bit)',
'compatibility': 'vmx-19',
'uuid': '422eba9f-0327-2da0-b546-f5a5e5dfdb68'
},
{
'vm_name': 'vmname03',
'hostsystem': 'esxi01',
'powerstate': 'poweredOn',
'guest_os': 'Other 3.x or later Linux (64-bit)',
'compatibility': 'vmx-11',
'uuid': '422e3863-b7de-dc55-74f8-6dc9c159b36d'
}
]
if section_esx_vsphere_virtual_machines is not None:
for vm in section_esx_vsphere_virtual_machines:
vm_name: str = adjust_name(vm['vm_name'], params.get(PARAM_VM_NAMES, {}))
if params.get(PARAM_DONT_ADD_VC_AS_VM) is True and vm_name.lower() == vsphere_host.lower():
continue
host_system: str = adjust_name(vm['hostsystem'], params.get(PARAM_HOST_SYSTEMS, {}))
add_host(host=vm_name)
add_connection(vm_name, host_system)
service_prefix: str = ''
ds_services = None
ls_connection = LiveStatusConnection()
if section_esx_vsphere_datastores is not None:
data_stores_host: str = 'data_stores' # Anchor for data stores
add_host(
host=data_stores_host,
link2core=False,
emblem=get_emblem(EMBLEM_DATA_STORE, params.get(PARAM_DATA_STORE)),
)
for ds_name in section_esx_vsphere_datastores:
add_host(
host=ds_name,
link2core=False,
emblem=get_emblem(EMBLEM_DATA_STORE, params.get(PARAM_DATA_STORE)),
)
add_connection(ds_name, data_stores_host)
if params.get(PARAM_DATA_STORE_AS_SERVICE) is True:
query: str = (
'GET services\n'
'Columns: service_description\n'
'Filter: service_check_command ~ ^check_mk-esx_vsphere_datastores(!.*)?\n'
f'Filter: host_name = {vsphere_host}\n'
'OutputFormat: python3\n'
)
if (ds_services := ls_connection.query(query=query)) is not None:
for ds_service in ds_services:
service_name: str = ds_service[0]
service_id: str = f'{service_name}@{vsphere_host}'
ds_name: str = service_name.split('fs_')[-1].split('Filesystem ')[-1]
add_service(
host=vsphere_host,
service=service_name,
)
add_connection(service_id, ds_name)
service_prefix = service_name.split(ds_name)[0]
query: str = (
'GET services\n'
'Columns: host_name long_plugin_output\n'
'Filter: description = ESX Datastores\n'
'OutputFormat: python3\n'
)
if (vm_to_data_stores := ls_connection.query(query=query)) is not None:
for vm, data_stores in vm_to_data_stores:
vm_name: str = adjust_name(vm, params.get(PARAM_VM_NAMES, {}))
if params.get(PARAM_DONT_ADD_VC_AS_VM) is True and vm_name.lower() == vsphere_host.lower():
continue
if vm_name in objects:
data_stores = data_stores.split(',')
for data_store in data_stores:
ds_name = data_store.strip().split(' ')[2]
if ds_services is not None:
service_name = f'{service_prefix}{ds_name}'
service_id = f'{service_name}@{vsphere_host}'
add_connection(service_id, vm_name)
else:
add_connection(ds_name, vm_name)
connections.sort()
data_set_vsphere = {
'version': 1,
'name': 'vSphere',
'objects': dict(sorted(objects.items())),
'connections': connections,
}
# workaround for missing topology
path: str = f'{OMD_ROOT}/var/check_mk/topology/data/{vsphere_host}'
if params.get(PARAM_ADD_DUMMY_TOPOLOGIES) is True:
data_sets = [
{'version': 1, 'name': 'cdp', 'objects': {}, 'connections': []},
{'version': 1, 'name': 'lldp', 'objects': {}, 'connections': []},
{'version': 1, 'name': 'static', 'objects': {}, 'connections': []},
{'version': 1, 'name': 'l3v4', 'objects': {}, 'connections': []},
data_set_vsphere
]
else:
data_sets = [data_set_vsphere]
for data_set in data_sets:
file: str = f'data_{data_set["name"].lower()}.json'
save_data_to_file(
data=data_set,
file=file,
make_default=params.get(PARAM_MAKE_DEFAULT, False),
path=path,
)
yield Result(state=State.OK, summary=f'Objects: {len(objects)}')
yield Result(state=State.OK, summary=f'Connections: {len(connections)}')
yield Result(state=State.OK, notice=f'Written to: {path}/{file}')
yield Result(state=State.OK, summary=f'Time taken: {((time_ns() - start_time) / 1e9):.2}/s')
check_plugin_vsphere_topo = CheckPlugin(
name="vsphere_topo",
service_name="vSphere Topology",
sections=[
'esx_vsphere_clusters',
'esx_vsphere_virtual_machines',
'esx_vsphere_datastores',
],
check_function=check_vsphere_topo,
discovery_function=discover_vsphere_topo,
check_ruleset_name=RULE_SET_NAME_VSPHERE_TOPO,
check_default_parameters={},
)
#!/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 : 2024-07-06
# File : vsphere_topo/lib/utils.py
from collections.abc import Mapping, MutableMapping, MutableSequence
from json import dumps as json_dunps
from os import environ
from pathlib import Path
from typing import Any, Final, Tuple
from livestatus import MultiSiteConnection, SiteConfigurations, SiteId
EMBLEM_CLUSTER: Final[str] = 'icon_plugins_hw'
EMBLEM_DATA_CENTER: Final[str] = 'icon_cloud'
EMBLEM_DATA_STORE: Final[str] = 'icon_services_green'
PARAM_ADD_DUMMY_TOPOLOGIES: Final[str] = 'add_dummy_topologies'
PARAM_CHANGE_CASE: Final[str] = 'change_case'
PARAM_CLUSTER: Final[str] = 'cluster'
PARAM_CUSTOM_EMBLEM: Final[str] = 'custom_emblem'
PARAM_DATA_CENTER: Final[str] = 'data_center'
PARAM_DATA_STORE: Final[str] = 'data_store'
PARAM_DATA_STORE_AS_SERVICE: Final[str] = 'data_sore_as_service'
PARAM_DEFAULT_EMBLEM: Final[str] = 'default_emblem'
PARAM_DONT_ADD_VC_AS_VM: Final[str] = 'dont_add_vc_as_vm'
PARAM_HOST_SYSTEMS: Final[str] = 'host_systems'
PARAM_KEEP_DOMAIN: Final[str] = 'keep_domain'
PARAM_LOWER: Final[str] = 'lower'
PARAM_MAKE_DEFAULT: Final[str] = 'make_default'
PARAM_NO_CHANGE: Final[str] = 'no_change'
PARAM_NO_EMBLEM: Final[str] = 'no_emblem'
PARAM_PREFIX: Final[str] = 'prefix'
PARAM_UPPER: Final[str] = 'upper'
PARAM_VM_NAMES: Final[str] = 'vm_names'
RULE_SET_NAME_VSPHERE_TOPO: Final[str] = 'vsphere_topo'
OMD_ROOT = environ["OMD_ROOT"]
def adjust_name(name: str, params: Mapping[str, Any]) -> str:
class Case:
upper: str = PARAM_UPPER
lower: str = PARAM_LOWER
no_change: str = PARAM_NO_CHANGE
if params.get(PARAM_KEEP_DOMAIN) is None:
name = name.split('.')[0]
match params.get(PARAM_CHANGE_CASE, PARAM_NO_CHANGE):
case Case.upper:
name = name.upper()
case Case.lower:
name = name.lower()
case Case.no_change | _: # all else
pass
if (prefix := params.get(PARAM_PREFIX)) is not None:
name = f'{prefix.strip()}{name.strip()}'
return name.strip()
def get_emblem(emblem: str, params: Tuple[str, bool | str] | None = None) -> str | None:
class Emblem:
no_emblem: str = PARAM_NO_EMBLEM
default_emblem: str = PARAM_DEFAULT_EMBLEM
custom_emblem: str = PARAM_CUSTOM_EMBLEM
match params:
case None:
return emblem
case (Emblem.no_emblem, True):
return None
case (Emblem.default_emblem, True):
return emblem
case _: # custom_emblem
return params[1].strip()
def save_data_to_file(data: Mapping, path: str, file: str, make_default: bool) -> None:
"""
Save the data as json file.
Args:
data: the topology data
path: the path were to save the data
file: the file name to save the data in
make_default: if True, create the symlink "default" with path as target
Returns:
None
"""
path_file = f'{path}/{file}'
save_file = Path(f'{path_file}')
save_file.parent.mkdir(exist_ok=True, parents=True)
save_file.write_text(json_dunps(data))
parent_path = Path(f'{path}').parent
if not Path(f'{parent_path}/default').exists():
make_default = True
if make_default:
Path(f'{parent_path}/default').unlink(missing_ok=True)
Path(f'{parent_path}/default').symlink_to(target=Path(path), target_is_directory=True)
#
# live status
#
class LiveStatusConnection(object):
def __init__(self):
self.sites: SiteConfigurations = SiteConfigurations({})
self.sites_mk = Path(f'{OMD_ROOT}/etc/check_mk/multisite.d/sites.mk')
self.socket_path = f'unix:{OMD_ROOT}/tmp/run/live'
if self.sites_mk.exists():
# make eval() "secure"
# https://realpython.com/python-eval-function/#minimizing-the-security-issues-of-eval
_code = compile(self.sites_mk.read_text(), "<string>", "eval")
allowed_names = ['sites', 'update']
for name in _code.co_names:
if name not in allowed_names:
raise NameError(f'Use of {name} in {self.sites_mk.name} not allowed.')
sites_raw: MutableMapping = {}
eval(self.sites_mk.read_text(), {"__builtins__": {}}, {"sites": sites_raw})
for site, data in sites_raw.items():
self.sites.update({site: {
'alias': data['alias'],
'timeout': data['timeout'],
}})
if data['socket'] == ('local', None):
self.sites[site]['socket'] = self.socket_path
else:
protocol, socket = data['socket']
address, port = socket['address']
self.sites[site]['socket'] = f'{protocol}:{address}:{port}'
self.sites[site]['tls'] = socket['tls']
else:
self.sites.update({SiteId('local'): {
'alias': 'Local site',
'timeout': 5,
'socket': self.socket_path
}})
self.c = MultiSiteConnection(self.sites)
dead_sites = [site['site']['alias'] for site in self.c.dead_sites().values()]
if dead_sites:
self.c.set_only_sites(self.c.alive_sites())
def query(self, query: str):
data: MutableSequence[Tuple[str, str]] = self.c.query(query=query)
if data:
return data
else:
return None
#!/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 : 2024-07-06
# File : vsphere_topo/rulesets/packages.py
from collections.abc import Mapping, Sequence
from cmk.rulesets.v1 import Label, Title
from cmk.rulesets.v1.form_specs import (
CascadingSingleChoice,
CascadingSingleChoiceElement,
DefaultValue,
DictElement,
Dictionary,
FixedValue,
SingleChoice,
SingleChoiceElement,
String,
)
from cmk.rulesets.v1.form_specs.validators import LengthInRange
from cmk.rulesets.v1.rule_specs import CheckParameters, HostCondition, Topic
from cmk_addons.plugins.vsphere_topo.lib.utils import (
EMBLEM_CLUSTER,
EMBLEM_DATA_CENTER,
EMBLEM_DATA_STORE,
PARAM_ADD_DUMMY_TOPOLOGIES,
PARAM_CHANGE_CASE,
PARAM_CLUSTER,
PARAM_CUSTOM_EMBLEM,
PARAM_DATA_CENTER,
PARAM_DATA_STORE,
PARAM_DATA_STORE_AS_SERVICE,
PARAM_DEFAULT_EMBLEM,
PARAM_DONT_ADD_VC_AS_VM,
PARAM_HOST_SYSTEMS,
PARAM_KEEP_DOMAIN,
PARAM_LOWER,
PARAM_MAKE_DEFAULT,
PARAM_NO_CHANGE,
PARAM_NO_EMBLEM,
PARAM_PREFIX,
PARAM_UPPER,
PARAM_VM_NAMES,
RULE_SET_NAME_VSPHERE_TOPO,
)
adjust_names_elements: Mapping[str, DictElement] = {
PARAM_KEEP_DOMAIN: DictElement(
parameter_form=FixedValue(
title=Title('Keep domain name'),
label=Label('The domain name will not be cut'),
value=True,
)),
PARAM_CHANGE_CASE: DictElement(
parameter_form=SingleChoice(
title=Title('Change case'),
elements=[
SingleChoiceElement(name=PARAM_UPPER, title=Title('All upper case')),
SingleChoiceElement(name=PARAM_LOWER, title=Title('All lower case')),
SingleChoiceElement(name=PARAM_NO_CHANGE, title=Title('Don\'t change case'))
],
prefill=DefaultValue(PARAM_NO_CHANGE)
)),
PARAM_PREFIX: DictElement(
parameter_form=String(
title=Title('Name prefix'),
custom_validate=(LengthInRange(min_value=1),)
)),
}
def get_emblem_element(default_emblem) -> Sequence[CascadingSingleChoiceElement]:
return [
CascadingSingleChoiceElement(
name=PARAM_NO_EMBLEM,
title=Title('No custom emblem'),
parameter_form=FixedValue(
value=True,
label=Label('No custom emblem will be used')
)),
CascadingSingleChoiceElement(
name=PARAM_DEFAULT_EMBLEM,
title=Title('Use default emblem'),
parameter_form=FixedValue(
value=True,
label=Label(f'Emblem "{default_emblem}" will be used')
)),
CascadingSingleChoiceElement(
name=PARAM_CUSTOM_EMBLEM,
title=Title('Use custom emblem'),
parameter_form=String(
custom_validate=(LengthInRange(min_value=1),),
prefill=DefaultValue(default_emblem),
))
]
def _parameter_form() -> Dictionary:
return Dictionary(
elements={
PARAM_HOST_SYSTEMS: DictElement(
parameter_form=Dictionary(
title=Title('Adjust ESXi host names'),
elements=adjust_names_elements
)),
PARAM_VM_NAMES: DictElement(
parameter_form=Dictionary(
title=Title('Adjust VM names'),
elements=adjust_names_elements
)),
PARAM_CLUSTER: DictElement(
parameter_form=CascadingSingleChoice(
title=Title('Cluster emblem'),
elements=get_emblem_element(EMBLEM_CLUSTER),
prefill=DefaultValue(PARAM_DEFAULT_EMBLEM)
)),
PARAM_DATA_CENTER: DictElement(
parameter_form=CascadingSingleChoice(
title=Title('Datacenter emblem'),
elements=get_emblem_element(EMBLEM_DATA_CENTER),
prefill=DefaultValue(PARAM_DEFAULT_EMBLEM)
)),
PARAM_DATA_STORE: DictElement(
parameter_form=CascadingSingleChoice(
title=Title('Datastore emblem'),
elements=get_emblem_element(EMBLEM_DATA_STORE),
prefill=DefaultValue(PARAM_DEFAULT_EMBLEM)
)),
PARAM_DATA_STORE_AS_SERVICE: DictElement(
parameter_form=FixedValue(
title=Title('Add data store service'),
label=Label('enabled'),
value=True
)),
PARAM_MAKE_DEFAULT: DictElement(
parameter_form=FixedValue(
title=Title('Make default'),
label=Label('This will be the default topology'),
value=True
)),
PARAM_DONT_ADD_VC_AS_VM: DictElement(
parameter_form=FixedValue(
title=Title('Don\'t add vCenter as VM'),
label=Label('The vCenter will not be added as VM'),
value=True
)),
PARAM_ADD_DUMMY_TOPOLOGIES: DictElement(
parameter_form=FixedValue(
title=Title('Add dummy topologies'),
label=Label('Adds empty CDP, LLDP, L3v4 and STATIC topology'),
value=True
)),
}
)
rule_spec_vsphere_topo = CheckParameters(
name=RULE_SET_NAME_VSPHERE_TOPO,
topic=Topic.APPLICATIONS,
parameter_form=_parameter_form,
title=Title('vSphere Topology'),
condition=HostCondition(),
)
{'author': 'Th.L. (thl-cmk[at]outlook[dot]com)',
'description': 'Creates the vSphere topology, based on the data from the ESXI '
'special agent (esx_vsphere_clusters, '
'esx_vsphere_virtual_machines sections).\n'
'\n'
'Topolgy is created by: vCenter -> data center(s) '
'->Cluster(s) -> ESX host(s) -> VM(s)\n',
'download_url': 'https://thl-cmk.hopto.org',
'files': {'cmk_addons_plugins': ['vsphere_topo/agent_based/packages.py',
'vsphere_topo/rulesets/packages.py',
'vsphere_topo/lib/utils.py']},
'name': 'vsphere_topo',
'title': 'vSphere Topologie',
'version': '0.0.1-20240709',
'version.min_required': '2.3.0b1',
'version.packaged': 'cmk-mkp-tool 0.2.0',
'version.usable_until': '2.4.0b1'}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment