diff --git a/README.md b/README.md index 604763caf3880f8847f03dd33eb9295eca394dca..9e10c1c352aa704d075f213414efa769c4ed7f26 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[PACKAGE]: ../../raw/master/mkp/nvdct-0.8.8-20240508.mkp "nvdct-0.8.8-20240508.mkp" +[PACKAGE]: ../../raw/master/mkp/nvdct-0.8.9-20240518.mkp "nvdct-0.8.9-20240518.mkp" Network Visualization Data Creation Tool (NVDCT) 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/mkp/nvdct-0.8.9-20240518.mkp b/mkp/nvdct-0.8.9-20240518.mkp new file mode 100644 index 0000000000000000000000000000000000000000..56d6a36ea2f20ee8588ad32227b3487e7599da62 Binary files /dev/null and b/mkp/nvdct-0.8.9-20240518.mkp differ diff --git a/source/bin/nvdct/lib/utils.py b/source/bin/nvdct/lib/utils.py index 4ec081232e5da05a887eeaeb0fceda5266b3cd3c..49f29860e87225d3325dddc53f49b18629a7a61c 100755 --- a/source/bin/nvdct/lib/utils.py +++ b/source/bin/nvdct/lib/utils.py @@ -22,7 +22,7 @@ from time import time as now_time from tomllib import loads as toml_loads, TOMLDecodeError from typing import List, Dict, Any, TextIO -NVDCT_VERSION = '0.8.8-20240507' +NVDCT_VERSION = '0.8.9-20240518' @unique diff --git a/source/bin/nvdct/nvdct.py b/source/bin/nvdct/nvdct.py index ffa5fb1141f0157afdb8bc0092bdc6d4f04316b9..9b564ed90bb9be2b197691435cc934e8e25522ca 100755 --- a/source/bin/nvdct/nvdct.py +++ b/source/bin/nvdct/nvdct.py @@ -86,7 +86,7 @@ # min inv_lldp_cache version: 0.9.3-20240320 -> host label nvdct/has_lldp_neighbours # 2024-03-27: added option --api-port, defaults to 80 # 2024-03-28: changed restapi get_interface_data to use one call to fetch all data -# 2024-03-29: added L3V4_IGNORE_IP and L3V4_IRNORE_WILDCARD to toml file +# 2024-03-29: added L3V4_IGNORE_IP and L3V4_IGNORE_WILDCARD to toml file # 2024-03-31: fixed: L3V3_REPLACE -> L3V4_REPLACE # made input from toml more bullet proof # 2024-04-03: change HostCacheMultiSite to be child of HostCacheLiveStatus @@ -102,6 +102,10 @@ # added tooltip for --skip-l3-if and --skip-l3-ip # 2024-04-30: automatically create @default if it doesn't exist # 2024-05-07: fixed handling of ignore wildcard in L3v4 topology +# 2024-05-17: added support for Extreme interfaces in LLDP without stack id (1 -> 1:1) +# 2024-05-18: added support for NXOS interfaces in LLDP in short from (Eth1/1 -> Ethernet1/1) +# added IP-Address/IP-Network as quickinfo +# 2024-05-18: fixed crash non empty neighbour port (ThX to andreas doehler) # creating topology data json from inventory data # @@ -142,8 +146,8 @@ __objects = { # "link": {}, # object not in CMK core 'metadata': { 'images': { - 'icon': 'icon_warning', # additional immage shown at the node - 'emblem': 'ip-network_80', # image to show for a object + 'icon': 'icon_warning', # additional image shown at the node + 'emblem': 'ip-network_80', # image to show for an object }, 'tooltip': { 'html': "Test message<div style='color: blue'>Some color <b>test</b></div>", @@ -263,7 +267,8 @@ def add_host_object(host: str, emblem: bool = False) -> None: 'images': { 'emblem': EMBLEMS.host_node, # node image # 'icon': 'icon_tick', # status image, top left from emblem - } + }, + **({'tooltip': {'quickinfo': [{'name': 'Host node', 'value': host}]}} if not link else {}) } NV_OBJECTS[host] = { @@ -342,7 +347,8 @@ def add_service_object( 'images': { 'emblem': EMBLEMS.service_node, # node image # 'icon': 'icon_tick', # status image, top left from emblem - } + }, + **({'tooltip': {'quickinfo': [{'name': 'Service node', 'value': item}]}} if not link else {}) }) if isinstance(native_speed, int): metadata.update({'native_speed': native_speed}) @@ -368,6 +374,11 @@ def add_ipv4_network_object(network: str, emblem: str | None = None) -> None: 'images': { 'emblem': emblem, # node image # 'icon': 'icon_tick', # status image, top left from emblem + }, + 'tooltip': { + 'quickinfo': [ + {'name': 'IP-Network', 'value': network}, + ] } } } @@ -387,6 +398,11 @@ def add_ipv4_address_object(host: str, interface: str | None, ipv4_address: str) 'images': { 'emblem': EMBLEMS.ip_address, # 'icon': 'icon_tick', + }, + 'tooltip': { + 'quickinfo': [ + {'name': 'IP-Address', 'value': ipv4_address}, + ] } } } @@ -600,26 +616,51 @@ def get_service_by_interface(host: str, interface: str) -> Tuple[str | None, str Returns: Tuple of interface item, native interface speed in bits per second """ + + _alternate_if_name = { + 'ethernet': 'eth', + # 'fastethernet': 'Fa', + # 'gigabitethernet': 'Gi', + # 'tengigabitethernet': 'Te', + # 'fortygigabitethernet': 'Fo', + # 'hundredgigabitethernet': 'Hu', + # 'management': 'Ma', + } + + def _get_short_if_name(interface: str) -> str: + """ + returns short interface name from long interface name + interface: is the long interface name + :type interface: str + """ + if not interface: + return interface + for interface_prefix in _alternate_if_name.keys(): + if interface.lower().startswith(interface_prefix.lower()): + interface_short = _alternate_if_name[interface_prefix] + return interface.lower().replace(interface_prefix.lower(), interface_short, 1) + return interface + # try to find the item for an interface - def _match_entry_with_item(_entry: Dict[str, str], services: List[str]) -> str | None: + def _match_entry_with_item(_entry: Dict[str, str], services: List[str]) -> Tuple[str | None, str | None]: values = [_entry.get('name'), _entry.get('description'), _entry.get('alias')] for value in values: if value in services: - return value + return value, _entry.get('speed') index = str(_entry.get('index')) # for index try with padding for i in range(1, 6): index_padded = f'{index:0>{i}}' if index_padded in services: - return index_padded + return index_padded, _entry.get('speed') # still not found try values + index for value in values: if f'{value} {index_padded}' in services: - return f'{value} {index_padded}' + return f'{value} {index_padded}', _entry.get('speed') LOGGER.warning(f'no match found |{_entry}|{services}|') - return None + return None, None # empty host/neighbour should never happen here if not host: @@ -657,14 +698,12 @@ def get_service_by_interface(host: str, interface: str) -> Tuple[str | None, str str(_entry.get('index')), _entry.get('phys_address'), ]: - _interface = _match_entry_with_item(_entry, interface_items) - if _interface: - native_speed = _entry.get('speed') - LOGGER.debug( - msg=f'Device: {host}: speed for interface ' - f'{interface} is {native_speed}' - ) - return _interface, native_speed + return _match_entry_with_item(_entry, interface_items) + elif f'1:{interface}' == _entry.get('name'): # Extreme non stack: + return _match_entry_with_item(_entry, interface_items) + elif _entry.get('name') is not None and _get_short_if_name( + _entry.get('name')) == interface.lower(): # Cisco NXOS + return _match_entry_with_item(_entry, interface_items) LOGGER.warning(msg=f'Device: {host}: service for interface {interface} not found') return None, None @@ -679,8 +718,12 @@ def create_device_from_inv( data: Dict = {'connections': {}, "interfaces": []} for topo_neighbour in inv_data: - neighbour = topo_neighbour.get(inv_columns.neighbour) - if not neighbour: + # check if required data are not empty + if not (neighbour := topo_neighbour.get(inv_columns.neighbour)): + continue + if not (raw_local_port := topo_neighbour.get(inv_columns.local_port)): + continue + if not (raw_neighbour_port := topo_neighbour.get(inv_columns.neighbour_port)): continue # drop neighbour if inventory neighbour is invalid @@ -711,7 +754,6 @@ def create_device_from_inv( neighbour = HOST_MAP[neighbour] # getting/checking interfaces - raw_local_port = topo_neighbour.get(inv_columns.local_port, '') local_port, _local_speed = get_service_by_interface(host, raw_local_port) if not local_port: local_port = raw_local_port @@ -722,7 +764,6 @@ def create_device_from_inv( msg=f'host: {host}, raw_local_port: {raw_local_port} -> local_port: {local_port}' ) - raw_neighbour_port = topo_neighbour.get(inv_columns.neighbour_port, '') neighbour_port, _neighbour_speed = get_service_by_interface(neighbour, raw_neighbour_port) if not neighbour_port: neighbour_port = raw_neighbour_port diff --git a/source/packages/nvdct b/source/packages/nvdct index 36a3ef5a3c128ade5fd2d2ae51fc7c400ff452e6..d046438ded8d21415894265df66250a6444c253a 100644 --- a/source/packages/nvdct +++ b/source/packages/nvdct @@ -57,7 +57,7 @@ 'htdocs/images/icons/location_80.png']}, 'name': 'nvdct', 'title': 'Network Visualization Data Creation Tool (NVDCT)', - 'version': '0.8.8-20240508', + 'version': '0.8.9-20240518', 'version.min_required': '2.2.0b1', 'version.packaged': '2.2.0p24', 'version.usable_until': '2.4.0p1'}