diff --git a/README.md b/README.md index e38ee2fd6af62b436aa914e299f6519d79e0be01..fb19868302e206c714089b8a7abe1cb926df9b78 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[PACKAGE]: ../../raw/master/create_topology_data-0.0.7-20231019.mkp "create_topology_data-0.0.7-20231019.mkp" +[PACKAGE]: ../../raw/master/create_topology_data-0.0.8-20231020.mkp "create_topology_data-0.0.8-20231020.mkp" # PoC for Network Visualization data creation from inventory data This script creates the topology data file needed for the [Checkmk Exchange Network visualization](https://forum.checkmk.com/u/schnetz) 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 4e3ad73d70ecd3682088e5ffb9381a6613d49d88..145a84d72ddcd81092978bbba6319e81e80fdc29 100755 --- a/bin/topology_data/create_topology_classes.py +++ b/bin/topology_data/create_topology_classes.py @@ -12,7 +12,28 @@ from os import environ from time import strftime from typing import Dict, List, Any, NamedTuple -from create_topology_utils import lldp_path, lldp_columns, lldp_label, user_data_file +from enum import Enum, unique +from create_topology_utils import ( + cdp_path, + cdp_columns, + cdp_label, + lldp_path, + lldp_columns, + lldp_label, + user_data_file, +) + + +class TopologyParams(NamedTuple): + path: str + columns: str + label: str + + +@unique +class Topologies(Enum): + CDP = TopologyParams(path=cdp_path, columns=cdp_columns, label=cdp_label) + LLDP = TopologyParams(path=lldp_path, columns=lldp_columns, label=lldp_label) class InventoryColumns(NamedTuple): @@ -51,13 +72,14 @@ class Settings: self.__settings = { 'seed_devices': None, - 'path_in_inventory': 'networking,cdp_cache', - 'inventory_columns': 'device_id,local_port,device_port', - 'data_source': 'inv_CDP', + 'path_in_inventory': cdp_path, + 'inventory_columns': cdp_columns, + 'data_source': cdp_label, 'time_format': '%Y-%m-%dT%H:%M:%S.%m', 'user_data_file': f'{self.__omd_root}/local/bin/topology_data/{user_data_file}', + 'merge': None, 'output_directory': None, - 'make_default': False, + 'default': False, 'keep_domain': False, 'lowercase': False, 'uppercase': False, @@ -79,11 +101,29 @@ class Settings: self.__settings['inventory_columns'] = lldp_columns # Then update values with cli values self.__settings.update(self.__args) + if not self.merge: + if not self.lldp: + self.merge = ['CDP'] + else: + self.merge = ['LLDP'] + + def set_topology_param(self, topology: TopologyParams): + self.__settings['path_in_inventory'] = topology.path + self.__settings['inventory_columns'] = topology.columns + self.__settings['data_source'] = topology.label @property def version(self) -> bool: return self.__settings['version'] + @property + def merge(self) -> List[str] | None: + return self.__settings['merge'] + + @merge.setter + def merge(self, topologies: List[str]): + self.__settings['merge'] = topologies + @property def keep(self) -> int | None: if self.__settings['keep']: @@ -113,8 +153,8 @@ class Settings: return self.__settings['dont_compare'] @property - def make_default(self) -> bool: - return self.__settings['make_default'] + def default(self) -> bool: + return self.__settings['default'] @property def keep_domain(self) -> bool: diff --git a/bin/topology_data/create_topology_data.py b/bin/topology_data/create_topology_data.py index 563e8c17c40ec383707a7fef3d16880beb196165..0ac0a999499419e1256be149db302a72bc1ab4b7 100755 --- a/bin/topology_data/create_topology_data.py +++ b/bin/topology_data/create_topology_data.py @@ -21,6 +21,8 @@ # get interfaces from autochecks changed to live status query # 2023-10-19: cleanup removed "all" unused functions # added option -u, --user-data-file to provide a user specific data file +# 2023-10-20: changed option -m/--make-default to -d/--default (-m is needed for -m/--merge +# added -m/--merge option # # PoC for creating topology_data.json from inventory data @@ -63,15 +65,17 @@ from create_topology_utils import ( merge_topologies, save_topology, get_data_form_live_status, + ExitCodes, ) from create_topology_classes import ( InventoryColumns, Settings, StaticConnection, + Topologies, ) -CREATE_TOPOLOGY_VERSION = '0.0.7-202310119' +CREATE_TOPOLOGY_VERSION = '0.0.8-202310120' ITEMS = {} HOST_MAP: Dict[str, str] = {} @@ -293,14 +297,21 @@ def create_topology( if __name__ == '__main__': SETTINGS = Settings(vars(parse_arguments(CREATE_TOPOLOGY_VERSION))) + if SETTINGS.version: print(f'{Path(__file__).name} version: {CREATE_TOPOLOGY_VERSION}') - exit(0) + exit(code=ExitCodes.ok) if SETTINGS.check_user_data_only: user_data = get_data_from_toml(file=SETTINGS.user_data_file) print(f'Could read/parse the user data from {SETTINGS.user_data_file}') - exit(0) + exit(code=ExitCodes.OK) + + if SETTINGS.merge: + _merge = list(set(SETTINGS.merge.copy())) + if len(_merge) != len(SETTINGS.merge): + print(f'-m/--merge options must be unique. Use "-m cdp lldp" oder "-m lldp cdp"') + exit(code=ExitCodes.BAD_OPTION_LIST) print() print(f'Start time: {strftime(SETTINGS.time_format)}') @@ -309,28 +320,33 @@ if __name__ == '__main__': 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', []) + + final_topology = {} + while SETTINGS.merge: + SETTINGS.set_topology_param(Topologies[SETTINGS.merge[-1]].value) + topology = create_topology( + seed_devices=list(set(SETTINGS.seed_devices + user_data.get('SEED_DEVICES', []))), + path_in_inventory=SETTINGS.path_in_inventory, + inv_columns=SETTINGS.inventory_columns, + data_source=SETTINGS.data_source, + debug=SETTINGS.debug, + ) + if topology: + final_topology = merge_topologies(topo_pri=topology, topo_sec=final_topology) + SETTINGS.merge.remove(SETTINGS.merge[-1]) + STATIC_CONNECTIONS = create_static_connections(connections=user_data.get('STATIC_CONNECTIONS', [])) - topology = create_topology( - seed_devices=list(set(SETTINGS.seed_devices + user_data.get('SEED_DEVICES', []))), - path_in_inventory=SETTINGS.path_in_inventory, - inv_columns=SETTINGS.inventory_columns, - data_source=SETTINGS.data_source, - debug=SETTINGS.debug, - ) + final_topology = merge_topologies(topo_pri=STATIC_CONNECTIONS, topo_sec=final_topology) - if topology: - # merge topology with static connections - topology = merge_topologies(topo_pri=STATIC_CONNECTIONS, topo_sec=topology) - - save_topology( - data=topology, - base_directory=f'{SETTINGS.omd_root}/{SETTINGS.topology_save_path}', - output_directory=SETTINGS.output_directory, - topology_file_name=SETTINGS.topology_file_name, - dont_compare=SETTINGS.dont_compare, - make_default=SETTINGS.make_default, - ) + save_topology( + data=final_topology, + base_directory=f'{SETTINGS.omd_root}/{SETTINGS.topology_save_path}', + output_directory=SETTINGS.output_directory, + topology_file_name=SETTINGS.topology_file_name, + dont_compare=SETTINGS.dont_compare, + make_default=SETTINGS.default, + ) if SETTINGS.keep: remove_old_data( diff --git a/bin/topology_data/create_topology_utils.py b/bin/topology_data/create_topology_utils.py index 727dbe8035c1ab6a0fd4c120af962a5523242af6..bf6776fea969bb46e7e688d8f998ccdd950b21d0 100755 --- a/bin/topology_data/create_topology_utils.py +++ b/bin/topology_data/create_topology_utils.py @@ -9,7 +9,7 @@ # # options used -# -m --make-default +# -d --default # -o --output-directory # -s --seed-devices # -u --user-data-file @@ -43,6 +43,15 @@ from argparse import ( ArgumentParser, RawTextHelpFormatter, ) +from enum import Enum, unique + + +@unique +class ExitCodes(Enum): + OK = 0 + BAD_OPTION_LIST = 1 + BAD_TOML_FORMAT = 3 + script = '~/local/bin/network-topology/create_topology_data.py' sample_seeds = 'Core01 Core02' @@ -82,7 +91,7 @@ def get_data_from_toml(file: str, debug: bool = False) -> Dict: except TOMLDecodeError as e: print( f'ERROR: data file {toml_file} is not in valid TOML format! ({e}), (see https://toml.io/en/)') - exit(2) + exit(code=ExitCodes.BAD_TOML_FORMAT) else: print(f'WARNING: User data {file} not found.') if debug: @@ -325,16 +334,27 @@ def parse_arguments(create_topology_version: str) -> arg_Namespace: formatter_class=RawTextHelpFormatter, epilog='Usage:\n' 'for CDP (the default):\n' - f'{script} -s {sample_seeds} -m\n' + f'{script} -s {sample_seeds} -d\n' 'for LLDP:\n' - f'{script} -s {sample_seeds} -m --lldp\n', + f'{script} -s {sample_seeds} -d --lldp\n', ) command_group = parser.add_mutually_exclusive_group() parser.add_argument( - '-m', '--make-default', default=False, action='store_const', const=True, + '-d', '--default', default=False, action='store_const', const=True, help='Set the created topology data as default', ) + parser.add_argument( + '-m', '--merge', + nargs=2, + choices=['CDP', 'LLDP'], + help=f'Merge topologies. This runs the topology creation for CDP and LLDP.\n' + f'The topologies are then merged in the specified order.\n' + f'I.e. -m CDP LLDP merges the CDP topology into the LLDP topology, overwriting\n' + f'the LLDP data in case there are conflicts.\n' + f'NOTE: static connection data from the user file will always merged with the\n' + f' highest priority', + ) parser.add_argument( '-o', '--output-directory', type=str, help='Directory name where to save the topology data.\n' diff --git a/create_topology_data-0.0.8-20231020.mkp b/create_topology_data-0.0.8-20231020.mkp new file mode 100644 index 0000000000000000000000000000000000000000..5d22cfd2f9bca0ad9256fe5ac3185ffabf3039e1 Binary files /dev/null and b/create_topology_data-0.0.8-20231020.mkp differ diff --git a/packages/create_topology_data b/packages/create_topology_data index b7662baf696d082a3a463ccc6fd38a497fb28b37..85ce824cda9156d9599bf186b03c70a55e9aab33 100644 --- a/packages/create_topology_data +++ b/packages/create_topology_data @@ -10,14 +10,15 @@ 'tester ;-)\n' '\n' 'Features:\n' - '- Reading connection data from the Checkmk inventory\n' - '- Adding user defined connections for connections not ' - 'included in the inventory\n' - '- handling differences between inventory neighbor names and ' - 'checkmk hostnames\n' - '- optimized for my CDP (default) and LLDP cache plugins\n' - '- can also be used with custom inventory plugins\n' - '- clean up old topologies\n' + '- Reading connection data from the checkmk HW/SW inventory.\n' + '- Handling differences between inventory neighbor names and ' + 'checkmk host names.\n' + '- Add custom connections for connections that are not in the ' + 'inventory\n' + '- Optimized for my CDP (default) and LLDP cache plugins\n' + '- Read and merge CDP and LLDP topologies\n' + '- Can also be used with custom inventory plugins\n' + '- Clean up old topologies\n' '\n' 'For more information about the network visualization plugin ' 'see: \n' @@ -55,7 +56,7 @@ 'topology_data/create_topology_data.toml']}, 'name': 'create_topology_data', 'title': 'Network Visualization data creation', - 'version': '0.0.7-20231019', + 'version': '0.0.8-20231020', 'version.min_required': '2.2.0p1', 'version.packaged': '2.2.0p11', 'version.usable_until': '2.3.0p1'}