diff --git a/README.md b/README.md index d771a6678cc3f50901f9bb3ddaa10aa8cbb747cb..3f1306fe987d2bd8d89c54a48f30b3ab2ba5977e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[PACKAGE]: ../../raw/master/mkp/create_topology_data-0.2.0-20231116.mkp "create_topology_data-0.2.0-20231116.mkp" +[PACKAGE]: ../../raw/master/mkp/create_topology_data-0.3.0-20231128.mkp "create_topology_data-0.3.0-20231128.mkp" Network Visualization data creation tool from inventory data This script creates the topology data file needed for the [Checkmk Exchange Network visualization](https://exchange.checkmk.com/p/network-visualization) plugin by Andreas Boesl and [schnetz](https://exchange.checkmk.com/u/schnetz).\ diff --git a/bin/topology_data/create_topology_classes.py b/bin/topology_data/create_topology_classes.py index 2610eadb4383708fbb98a530117368f6a56a267b..6b3a176282105c78f4c8cc13aed21cc72e4303cf 100755 --- a/bin/topology_data/create_topology_classes.py +++ b/bin/topology_data/create_topology_classes.py @@ -16,6 +16,8 @@ from typing import Dict, List, Any, NamedTuple from enum import Enum, unique from abc import abstractmethod from ast import literal_eval +import livestatus + from create_topology_utils import ( CREATE_TOPOLOGY_VERSION, @@ -186,7 +188,7 @@ class Settings: class HostCache: - def __init__(self): + def __init__(self, debug: bool = False): self.__cache = {} self.__inventory_pre_fetch_list: List[str] = [ PATH_INTERFACES, @@ -202,6 +204,7 @@ class HostCache: Returns: the inventory data as dictionary """ + raise NotImplementedError() @abstractmethod def get_interface_items(self, host: str, debug: bool = False) -> List: @@ -214,6 +217,7 @@ class HostCache: Returns: list of the interface items """ + raise NotImplementedError() def __fill_cache(self, host: str): # pre fill inventory data @@ -337,3 +341,82 @@ class HostCacheFileSystem(HostCache): return [] return __data + + +class HostCacheMultiSite(HostCache): + def __init__(self, debug: bool = False): + super().__init__(debug=debug) + self.__sites = {} + self.get_sites(debug=debug) + self.__c = livestatus.MultiSiteConnection(self.__sites) + # self.__c.set_prepend_site(False) # is default + # self.__c.parallelize = True # is default + self.__dead_sites = [site['site']['alias'] for site in self.__c.dead_sites().values()] + if self.__dead_sites: + self.__dead_sites = ', '.join( self.__dead_sites) + print(f'WARNING: use of dead site(s) {self.__dead_sites} is disabled') + self.__c.set_only_sites(self.__c.alive_sites()) + + def get_sites(self, debug: bool = False): + sites_mk = Path(f'{OMD_ROOT}/etc/check_mk/multisite.d/sites.mk') + socket_path = f'unix:{OMD_ROOT}/tmp/run/live' + if sites_mk.exists(): + # make eval() "secure" + # https://realpython.com/python-eval-function/#minimizing-the-security-issues-of-eval + _code = compile(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 {sites_mk.name} not allowed.') + + sites_raw = {} + eval(sites_mk.read_text(), {"__builtins__": {}}, {"sites": sites_raw}) + + for site, data in sites_raw.items(): + self.__sites[site] = { + 'alias': data['alias'], + 'timeout': data['timeout'], + 'nagios_url': '/nagios/', + } + if data['socket'] == ('local', None): + self.__sites[site]['socket'] = 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'] + if debug: + print(f'Multisite: Site {site} found, ' + f'Socket: {self.__sites[site]["socket"]}, ' + f'TLS: {self.__sites[site].get("tls", "N/A")}.') + + def get_inventory_data(self, host: str, debug: bool = False) -> Dict[str, str] | None: + query = f'GET hosts\nColumns: mk_inventory\nOutputFormat: python3\nFilter: host_name = {host}\n' + data = self.__c.query(query=query) + if data: + try: + data = literal_eval(data[0][0].decode('utf-8')) + except SyntaxError as e: + if debug: + print(f'data: |{data}|') + print(f'type: {type(data)}') + print(f'exception: {e}') + return + return data + + def get_interface_items(self, host: str, debug: bool = False) -> List: + query = ( + 'GET services\n' + 'Columns: host_name description check_command\n' + 'Filter: description ~ Interface\n' + f'Filter: host_name = {host}\n' + 'OutputFormat: python3\n' + ) + data = self.__c.query(query=query) + items = [] + for host, description, check_command in data: + items.append(description[10:]) # remove 'Interface ' from description + + if debug: + print(f'Interfaces items found: {len(items)} an host {host}') + return items diff --git a/bin/topology_data/create_topology_data.py b/bin/topology_data/create_topology_data.py index e3c66277680ff40554adf1412c1bf68d0452f896..d3caccdd334fc00dae480a22de06d160e76f50b6 100755 --- a/bin/topology_data/create_topology_data.py +++ b/bin/topology_data/create_topology_data.py @@ -38,12 +38,12 @@ # 2023-11-16: added option -b/--backend [LIVESTATUS, FILESYSTEM], # LIVESTATUS is the default for performance reasons -> for now only local site # FILESYSTEM fethches the data directly form the inventory files -> use in distributed environments - +# 2023-11-28: added MULTISITE as backend, via livestatus # -# PoC for creating topology_data.json from inventory data +# creating topology_data.json from inventory data # # This script creates the topology data file needed for the Checkmk "network_visualization" plugin by -# Andreas Boesl and schnetz. For more information see +# Andreas Boesl/schnetz. For more information see # https://forum.checkmk.com/t/network-visualization/41680 # https://exchange.checkmk.com/p/network-visualization # @@ -76,6 +76,7 @@ from create_topology_classes import ( StaticConnection, HostCacheLiveStatus, HostCacheFileSystem, + HostCacheMultiSite, CacheItems, ) @@ -275,18 +276,20 @@ def create_topology( if __name__ == '__main__': start_time = time_ns() SETTINGS = Settings(vars(parse_arguments())) + print() + print(f'Start time: {strftime(SETTINGS.time_format)}') + match SETTINGS.backend: case 'LIVESTATUS': HOST_CACHE = HostCacheLiveStatus() case 'FILESYSTEM': HOST_CACHE = HostCacheFileSystem() + case 'MULTISITE': + HOST_CACHE = HostCacheMultiSite(debug=SETTINGS.debug) case _: print(f'Backend {SETTINGS.backend} not (yet) implemented') exit() - print() - print(f'Start time: {strftime(SETTINGS.time_format)}') - user_data = get_data_from_toml(file=SETTINGS.user_data_file) HOST_MAP = user_data.get('HOST_MAP', {}) DROP_HOSTS = user_data.get('DROP_HOSTS', []) diff --git a/mkp/create_topology_data-0.3.0-20231128.mkp b/mkp/create_topology_data-0.3.0-20231128.mkp new file mode 100644 index 0000000000000000000000000000000000000000..c56f1b9d2510820ca71ec418fcf8ed95361ddc67 Binary files /dev/null and b/mkp/create_topology_data-0.3.0-20231128.mkp differ diff --git a/packages/create_topology_data b/packages/create_topology_data index 3675ee3ba919fd43877bcc8af689081856a40dcc..9c199bfded9ea3c0421e6c880132febbbffc28e8 100644 --- a/packages/create_topology_data +++ b/packages/create_topology_data @@ -59,7 +59,7 @@ 'topology_data/create_topology_args.py']}, 'name': 'create_topology_data', 'title': 'Network Visualization data creation', - 'version': '0.2.0-20231116', + 'version': '0.3.0-20231128', 'version.min_required': '2.2.0p1', 'version.packaged': '2.2.0p14', 'version.usable_until': '2.3.0p1'}