From fbefde6b4bb7bf90be0218475e16b9d809f0770d Mon Sep 17 00:00:00 2001 From: "th.l" <thl-cmk@outlook.com> Date: Fri, 14 Feb 2025 22:50:15 +0100 Subject: [PATCH] fixed MKP, mising update_config.py Please enter the commit message for your changes. Lines starting --- README.md | 2 +- mkp/nvdct-0.9.9-20250214.mkp | Bin 0 -> 55556 bytes source/bin/nvdct/conf/nvdct.toml | 1 + source/bin/nvdct/lib/args.py | 68 +++-- source/bin/nvdct/lib/backends.py | 32 +- source/bin/nvdct/lib/constants.py | 23 +- source/bin/nvdct/lib/settings.py | 68 +++-- source/bin/nvdct/lib/topologies.py | 12 +- source/bin/nvdct/lib/update_config.py | 402 ++++++++++++++++++++++++++ source/bin/nvdct/lib/utils.py | 1 - source/bin/nvdct/nvdct.py | 2 + source/packages/nvdct | 5 +- 12 files changed, 520 insertions(+), 96 deletions(-) create mode 100644 mkp/nvdct-0.9.9-20250214.mkp create mode 100755 source/bin/nvdct/lib/update_config.py diff --git a/README.md b/README.md index e302456..4620c9d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[PACKAGE]: ../../raw/master/mkp/nvdct-0.9.8-20250205.mkp "nvdct-0.9.8-20250205.mkp" +[PACKAGE]: ../../raw/master/mkp/nvdct-0.9.9-20250214.mkp "nvdct-0.9.9-20250214.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.\ diff --git a/mkp/nvdct-0.9.9-20250214.mkp b/mkp/nvdct-0.9.9-20250214.mkp new file mode 100644 index 0000000000000000000000000000000000000000..979efdd9cd8f8c8682f257efb4380f9e70c2c7a9 GIT binary patch literal 55556 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 z	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 u00000000000000000000000000000000000006-MSN{Qw>J>Nuzykmy4?+(B literal 0 HcmV?d00001 diff --git a/source/bin/nvdct/conf/nvdct.toml b/source/bin/nvdct/conf/nvdct.toml index 524e395..6698fe7 100644 --- a/source/bin/nvdct/conf/nvdct.toml +++ b/source/bin/nvdct/conf/nvdct.toml @@ -168,6 +168,7 @@ FILTER_BY_SITE = [ # l2_case = "OFF" | "LOWER" | "UPPER" | "IGNORE" | "AUTO" # l2_display_neighbours = false | true # l2_display_ports = false | true +# l2_ignore_mismatch = ["DUPLEX", "SPEED", "VLAN"] # l2_prefix = "" # l2_remove_domain = "OFF" | "ON" | "AUTO" # l2_skip_external = false | true diff --git a/source/bin/nvdct/lib/args.py b/source/bin/nvdct/lib/args.py index 3f9a81f..8f2fa78 100755 --- a/source/bin/nvdct/lib/args.py +++ b/source/bin/nvdct/lib/args.py @@ -24,6 +24,7 @@ # --l2-case # --l2-display-ports # --l2-display-neighbours +# --l2-ignore-mismatch # --l2-prefix # --l2-remove-domain # --l2-skip-external @@ -55,16 +56,17 @@ from pathlib import Path from lib.constants import ( Backends, CONFIG_FILE, - Case, CliLong, CliShort, ExitCodes, IncludeExclude, + L2Case, + L2IgnoreMismatch, + L2RemoveDomain, Layers, LogLevels, MinVersions, NVDCT_VERSION, - RemoveDomain, SCRIPT, TIME_FORMAT_ARGPARSER, TomlSections, @@ -95,11 +97,11 @@ def parse_arguments() -> arg_Namespace: f' {ExitCodes.AUTOMATION_SECRET_NOT_FOUND} - Automation secret not found\n' f' {ExitCodes.NO_LAYER_CONFIGURED} - No layer to work on\n' '\nUsage:\n' - f'{SCRIPT} -u ~/local/bin/nvdct/conf/my_{CONFIG_FILE} \n\n' + f'{SCRIPT} -c ~/local/bin/nvdct/conf/my_{CONFIG_FILE} \n\n' ) parser.add_argument( CliShort.BACKEND, CliLong.BACKEND, - choices=[Backends.LIVESTATUS, Backends.MULTISITE, Backends.RESTAPI], + choices=Backends.list(), # default='MULTISITE', help='Backend used to retrieve the topology data\n' f' - {Backends.LIVESTATUS} : fetches data via local Livestatus (local site only)\n' @@ -119,13 +121,7 @@ def parse_arguments() -> arg_Namespace: parser.add_argument( CliShort.LAYERS, CliLong.LAYERS, nargs='+', - choices=[ - Layers.CDP, - Layers.LLDP, - Layers.L3V4, - Layers.STATIC, - ], - # default=['CDP'], + choices=Layers.list(), help=( f' - {Layers.CDP} : needs inv_cdp_cache package at least in version {MinVersions.CDP}\n' f' - {Layers.LLDP} : needs inv_lldp_cache package at least in version {MinVersions.LLDP}\n' @@ -166,14 +162,14 @@ def parse_arguments() -> arg_Namespace: ) parser.add_argument( CliLong.FILTER_CUSTOMERS, - choices=[IncludeExclude.INCLUDE, IncludeExclude.EXCLUDE], + choices=IncludeExclude.list(), # default='INCLUDE', help=f'{IncludeExclude.INCLUDE}/{IncludeExclude.EXCLUDE} customer list "[{TomlSections.FILTER_BY_CUSTOMER}]" from TOML file.' f'NOTE: {Backends.MULTISITE} backend only.', ) parser.add_argument( CliLong.FILTER_SITES, - choices=[IncludeExclude.EXCLUDE, IncludeExclude.EXCLUDE], + choices=IncludeExclude.list(), # default='INCLUDE', help=f'{IncludeExclude.INCLUDE}/{IncludeExclude.EXCLUDE} site list "[{TomlSections.FILTER_BY_SITE}]" from TOML file.' ) @@ -185,13 +181,13 @@ def parse_arguments() -> arg_Namespace: ) parser.add_argument( CliLong.L2_CASE, - choices=[Case.INSENSITIVE, Case.LOWER, Case.UPPER, Case.AUTO, Case.OFF], - help='Change L2 neighbour name case before matching to Checkmk host.\n' - f'- {Case.OFF} : Do not change the case of the neighbour name.' - f'- {Case.INSENSITIVE} : search for a matching host by ignoring the case of neighbour name and host name\n' - f'- {Case.LOWER} : change to all lower case\n' - f'- {Case.UPPER} : change to all upper case, without the domain part of the neighbour name\n' - f'- {Case.AUTO} : try all the above variants\n' + choices=L2Case.list(), + help='Change L2 neighbour name case before matching to Checkmk host\n' + f'- {L2Case.OFF} : Do not change the case of the neighbour name\n' + f'- {L2Case.INSENSITIVE} : search for a matching host by ignoring the case of neighbour name and host name\n' + f'- {L2Case.LOWER} : change to all lower case\n' + f'- {L2Case.UPPER} : change to all upper case, without the domain part of the neighbour name\n' + f'- {L2Case.AUTO} : try all the above variants\n' f'Default is let the case of the neighbour name untouched ("OFF").\n' f'Takes place after "{CliLong.L2_REMOVE_DOMAIN}" and before "{CliLong.L2_PREFIX}"', ) @@ -203,6 +199,16 @@ def parse_arguments() -> arg_Namespace: CliLong.L2_DISPLAY_NEIGHBOURS, action='store_const', const=True, # default=False, help='Use L2 neighbour name as display name in L2 topologies', ) + parser.add_argument( + CliLong.L2_IGNORE_MISMATCH, + nargs='+', + choices=L2IgnoreMismatch.list(), + help=( + f' - {L2IgnoreMismatch.DUPLEX} : Ignore duplex mismatch in layer 2 topologies\n' + f' - {L2IgnoreMismatch.SPEED} : Ignore speed mismatch in layer 2 topologies\n' + f' - {L2IgnoreMismatch.VLAN} : Ignore native vlan mismatch in layer 2 topologies\n' + ) + ) parser.add_argument( CliLong.L2_PREFIX, type=str, help=f'Prepends each L2 neighbour name with the prefix before matching to a Checkmk host name.\n' @@ -210,12 +216,12 @@ def parse_arguments() -> arg_Namespace: ) parser.add_argument( CliLong.L2_REMOVE_DOMAIN, - choices=[RemoveDomain.OFF, RemoveDomain.ON, RemoveDomain.AUTO], + choices=L2RemoveDomain.list(), help=f'Handle the the domain name part of a neighbour name before matching it to a Checkmk host.\n' - f'- {RemoveDomain.OFF} : dont touch the neighbour name, keep host name and domain part\n' - f'- {RemoveDomain.ON} : will remove the domain part from the neighbour name, keep only the host name part\n' - f'- {RemoveDomain.AUTO} : try all of the above variants\n' - f'Default: "{RemoveDomain.OFF}". Takes place after "{CliLong.L2_REMOVE_DOMAIN}" and before "{CliLong.L2_PREFIX}"', + f'- {L2RemoveDomain.OFF} : dont touch the neighbour name, keep host name and domain part\n' + f'- {L2RemoveDomain.ON} : will remove the domain part from the neighbour name, keep only the host name part\n' + f'- {L2RemoveDomain.AUTO} : try all of the above variants\n' + f'Default: "{L2RemoveDomain.OFF}". Takes place after "{CliLong.L2_REMOVE_DOMAIN}" and before "{CliLong.L2_PREFIX}"', ) parser.add_argument( CliLong.L2_SKIP_EXTERNAL, action='store_const', const=True, # default=False, @@ -260,15 +266,7 @@ def parse_arguments() -> arg_Namespace: parser.add_argument( CliLong.LOG_LEVEL, # nargs='+', - choices=[ - LogLevels.CRITICAL, - LogLevels.FATAL, - LogLevels.ERROR, - LogLevels.WARNING, - LogLevels.INFO, - LogLevels.DEBUG, - LogLevels.OFF - ], + choices=LogLevels.list(), # default='WARNING', help=f'Sets the log level. The default is "{LogLevels.WARNING}"' ) @@ -295,6 +293,6 @@ def parse_arguments() -> arg_Namespace: ) parser.add_argument( CliLong.UPDATE_CONFIG, action='store_const', const=True, # default=False, - help='Adjusts old options in TOML file.', + help='Adjusts options in config file.', ) return parser.parse_args() diff --git a/source/bin/nvdct/lib/backends.py b/source/bin/nvdct/lib/backends.py index 8fce63c..35860ed 100755 --- a/source/bin/nvdct/lib/backends.py +++ b/source/bin/nvdct/lib/backends.py @@ -31,16 +31,16 @@ from lib.constants import ( Backends, CACHE_INTERFACES_DATA, CacheItems, - Case, ExitCodes, HostFilter, IncludeExclude, InvPaths, + L2Case, L2InvColumns, + L2RemoveDomain, LiveStatusOperator, MIN_CMK_VERSION_POST, OMD_ROOT, - RemoveDomain, ) from lib.utils import ( LOGGER, @@ -62,7 +62,7 @@ class HostCache: LOGGER.info(f'{backend} init HOST_CACHE') self.cache: Dict = {} - self.neighbour_to_host: MutableMapping[str, str] = {} + self.neighbour_to_host: MutableMapping[str, str | None] = {} self._inventory_pre_fetch_list: List[str] = [InvPaths.INTERFACES] self.backend: str = str(backend) @@ -71,7 +71,7 @@ class HostCache: self.l2_neighbour_replace_regex: List[Tuple[str, str]] = [] self.pre_fetch: bool = bool(pre_fetch) self.prefix: str = '' - self.remove_domain: str = RemoveDomain.OFF + self.remove_domain: str = L2RemoveDomain.OFF self.filter_include: MutableSequence[str] = [] self.filter_exclude: MutableSequence[str] = [] self.no_case_host_map: MutableMapping[str, str] = {} @@ -94,9 +94,9 @@ class HostCache: def init_filter_lists( self, - filter_by_folder: Mapping[str, Sequence[str]], - filter_by_host_label: Mapping[str, Sequence[str]], - filter_by_host_tag: Mapping[str, Sequence[str]], + filter_by_folder: Mapping[str, set[str]], + filter_by_host_label: Mapping[str, set[str]], + filter_by_host_tag: Mapping[str, set[str]], ): for folder in filter_by_folder[IncludeExclude.INCLUDE]: self.filter_include += self.query_hosts_by_filter(HostFilter.FOLDER, folder, LiveStatusOperator.SUPERSET) @@ -243,7 +243,7 @@ class HostCache: Returns: None, the data is directly writen to self.cache """ - inventory_of_hosts: Mapping[str, Mapping | None] = self.get_inventory_data(hosts=hosts) + inventory_of_hosts: Mapping[str, Dict | None] = self.get_inventory_data(hosts=hosts) if inventory_of_hosts: for host, inventory in inventory_of_hosts.items(): if host not in self.cache: @@ -343,30 +343,30 @@ class HostCache: host_no_domain: str = host.split('.')[0] match self.remove_domain: - case RemoveDomain.ON: + case L2RemoveDomain.ON: LOGGER.debug(f'{self.backend} Remove domain: {host} -> {host_no_domain}') host = host_no_domain possible_hosts.add(host_no_domain) - case RemoveDomain.AUTO: + case L2RemoveDomain.AUTO: possible_hosts.add(host) possible_hosts.add(host_no_domain) - case RemoveDomain.OFF | _: + case L2RemoveDomain.OFF | _: possible_hosts.add(host) match self.case: - case Case.UPPER: + case L2Case.UPPER: if not '.' in host: # use UPPER only for names without domain LOGGER.debug(f'{self.backend} Change neighbour to upper case: {host} -> {host.upper()}') possible_hosts.add(host.upper) - case Case.LOWER: + case L2Case.LOWER: LOGGER.debug(f'{self.backend} Change neighbour to lower case: {host} -> {host.lower()}') possible_hosts.add(host.lower()) - case Case.AUTO: + case L2Case.AUTO: possible_hosts.add(host) possible_hosts.add(host.lower()) possible_hosts.add(host_no_domain.lower()) possible_hosts.add(host_no_domain.upper()) - case Case.OFF | _: + case L2Case.OFF | _: possible_hosts.add(host) possible_hosts = [f'{self.prefix}{host}' for host in possible_hosts] @@ -381,7 +381,7 @@ class HostCache: LOGGER.debug(f'{self.backend} Matched neighbour to host: |{neighbour}| -> |{entry}|') return entry - if self.case in [Case.AUTO, Case.INSENSITIVE]: + if self.case in [L2Case.AUTO, L2Case.INSENSITIVE]: if not self.no_case_host_map: self.no_case_host_map = {host.lower():host for host in self.cache} for entry in possible_hosts: diff --git a/source/bin/nvdct/lib/constants.py b/source/bin/nvdct/lib/constants.py index 579eb0d..2cac55c 100755 --- a/source/bin/nvdct/lib/constants.py +++ b/source/bin/nvdct/lib/constants.py @@ -13,7 +13,7 @@ from os import environ from typing import Final # -NVDCT_VERSION: Final[str] = '0.9.8-20250107' +NVDCT_VERSION: Final[str] = '0.9.9-20250207' # OMD_ROOT: Final[str] = environ["OMD_ROOT"] # @@ -21,19 +21,25 @@ API_PORT_DEFAULT: Final[int] = 5001 CACHE_INTERFACES_DATA: Final[str] = 'interface_data' CMK_SITE_CONF: Final[str] = f'{OMD_ROOT}/etc/omd/site.conf' CONFIG_FILE: Final[str] = 'nvdct.toml' +CONFIG_PATH_DEFAULT: Final[str] = f'{OMD_ROOT}/local/bin/nvdct/conf' DATAPATH: Final[str] = f'{OMD_ROOT}/var/check_mk/topology/data' LOGGER: Logger = getLogger('root)') -LOG_FILE_DEFAULT: Final[str] = f'{OMD_ROOT}/var/log/nvdct.log' +LOG_PATH_DEFAULT: Final[str] = f'{OMD_ROOT}/var/log' +LOG_FILE_DEFAULT: Final[str] = f'{LOG_PATH_DEFAULT}/nvdct.log' MIN_CMK_VERSION_POST: Final[str] = '2.3.0p23' SCRIPT: Final[str] = '~/local/bin/nvdct/nvdct.py' -TIME_FORMAT_ARGPARSER: Final[str] = '%%Y-%%m-%%dT%%H:%%M:%%S.%%m' TIME_FORMAT_DEFAULT: Final[str] = '%Y-%m-%dT%H:%M:%S.%m' +TIME_FORMAT_ARGPARSER: Final[str] = TIME_FORMAT_DEFAULT.replace('%', '%%') + class EnumValue(Enum): def __get__(self, instance, owner): return self.value + @classmethod + def list(cls): + return list(map(lambda c: c.value, cls)) @unique class ExitCodes(EnumValue): @@ -72,7 +78,7 @@ class Backends(EnumValue): @unique -class Case(EnumValue): +class L2Case(EnumValue): AUTO: Final[str] = 'AUTO' INSENSITIVE: Final[str] = 'INSENSITIVE' LOWER: Final[str] = 'LOWER' @@ -81,11 +87,16 @@ class Case(EnumValue): @unique -class RemoveDomain(EnumValue): +class L2RemoveDomain(EnumValue): ON: Final[str] = 'ON' OFF: Final[str] = 'OFF' AUTO: Final[str] = 'AUTO' +@unique +class L2IgnoreMismatch(EnumValue): + DUPLEX: Final[str] = 'DUPLEX' + SPEED: Final[str] = 'SPEED' + VLAN: Final[str] = 'VLAN' @unique class CacheItems(EnumValue): @@ -107,6 +118,7 @@ class CliLong(EnumValue): L2_CASE: Final[str] = '--l2-case' L2_DISPLAY_NEIGHBOURS: Final[str] = '--l2-display-neighbours' L2_DISPLAY_PORTS: Final[str] = '--l2-display-ports' + L2_IGNORE_MISMATCH: Final[str] = '--l2-ignore-mismatch' L2_PREFIX: Final[str] = '--l2-prefix' L2_REMOVE_DOMAIN: Final[str] = '--l2-remove-domain' L2_SKIP_EXTERNAL: Final[str] = '--l2-skip-external' @@ -285,6 +297,7 @@ class TomlSettings(EnumValue): L2_CASE: Final[str] = cli_long_to_toml(CliLong.L2_CASE) L2_DISPLAY_NEIGHBOURS: Final[str] = cli_long_to_toml(CliLong.L2_DISPLAY_NEIGHBOURS) L2_DISPLAY_PORTS: Final[str] = cli_long_to_toml(CliLong.L2_DISPLAY_PORTS) + L2_IGNORE_MISMATCH: Final[str] = cli_long_to_toml(CliLong.L2_IGNORE_MISMATCH) L2_PREFIX: Final[str] = cli_long_to_toml(CliLong.L2_PREFIX) L2_REMOVE_DOMAIN: Final[str] = cli_long_to_toml(CliLong.L2_REMOVE_DOMAIN) L2_SKIP_EXTERNAL: Final[str] = cli_long_to_toml(CliLong.L2_SKIP_EXTERNAL) diff --git a/source/bin/nvdct/lib/settings.py b/source/bin/nvdct/lib/settings.py index e069f0c..7957692 100755 --- a/source/bin/nvdct/lib/settings.py +++ b/source/bin/nvdct/lib/settings.py @@ -15,7 +15,6 @@ from collections.abc import Mapping from ipaddress import AddressValueError, NetmaskValueError, ip_address, ip_network from logging import CRITICAL, DEBUG, ERROR, FATAL, INFO, WARNING from pathlib import Path -from sys import exit as sys_exit from time import strftime from typing import Dict, List, NamedTuple, Set, Tuple @@ -23,16 +22,18 @@ from lib.constants import ( API_PORT_DEFAULT, Backends, CONFIG_FILE, - Case, + CONFIG_PATH_DEFAULT, EmblemNames, EmblemValues, - ExitCodes, IncludeExclude, + L2Case, + L2IgnoreMismatch, + L2RemoveDomain, + Layers, LOGGER, LOG_FILE_DEFAULT, + LOG_PATH_DEFAULT, LogLevels, - OMD_ROOT, - RemoveDomain, TIME_FORMAT_DEFAULT, TomlSections, TomlSettings, @@ -86,7 +87,7 @@ class Settings: TomlSettings.API_PORT: None, TomlSettings.BACKEND: Backends.MULTISITE, TomlSettings.CHECK_CONFIG: False, - TomlSettings.CONFIG: f'{OMD_ROOT}/local/bin/nvdct/conf/{CONFIG_FILE}', + TomlSettings.CONFIG: f'{CONFIG_PATH_DEFAULT}/{CONFIG_FILE}', TomlSettings.DEFAULT: False, TomlSettings.DONT_COMPARE: False, TomlSettings.FILTER_CUSTOMERS: None, @@ -94,6 +95,7 @@ class Settings: TomlSettings.KEEP_MAX_TOPOLOGIES: False, TomlSettings.L2_CASE: None, TomlSettings.L2_DISPLAY_PORTS: False, + TomlSettings.L2_IGNORE_MISMATCH: [], TomlSettings.L2_DISPLAY_NEIGHBOURS: False, TomlSettings.L2_PREFIX: None, TomlSettings.L2_REMOVE_DOMAIN: None, @@ -132,14 +134,6 @@ class Settings: self.__settings.update(self.__user_data.get(TomlSections.SETTINGS, {})) self.__settings.update(self.__args) - if self.layers: - layers = list(set(self.layers)) - if len(layers) != len(self.layers): - # logger not initialized here - # LOGGER.fatal('-l/--layers options must be unique. Don\'t use any layer more than once.') - print('-l/--layers options must be unique. Don\'t use any layer more than once.') - sys_exit(ExitCodes.BAD_OPTION_LIST) - self.__api_port: int | None = None # init user data with defaults @@ -150,6 +144,7 @@ class Settings: self.__filter_by_host_tag: Dict[str, Set[str]] | None = None self.__filter_by_site: List[str] | None = None self.__l2_drop_neighbours: List[str] | None = None + self.__l2_ignore_mismatch: List[str] | None = None self.__l2_neighbour_replace_regex: List[Tuple[str, str]] | None = None self.__l2_neighbour_to_host_map: Dict[str, str] | None = None self.__l2_seed_devices: List[str] | None = None @@ -158,6 +153,7 @@ class Settings: self.__l3_replace: Dict[str, str] | None = None self.__l3_summarize: List[ip_network] | None = None self.__l3v4_ignore_wildcard: List[Wildcard] | None = None + self.__layers: List[str] | None = None self.__map_speed_to_thickness: List[Thickness] | None = None self.__protected_topologies: List[str] | None = None self.__static_connections: List[StaticConnection] | None = None @@ -179,11 +175,7 @@ class Settings: @property # -b --backend def backend(self) -> str: - if str(self.__settings[TomlSettings.BACKEND]) in [ - Backends.LIVESTATUS, - Backends.MULTISITE, - Backends.RESTAPI - ]: + if str(self.__settings[TomlSettings.BACKEND]) in Backends.list(): return str(self.__settings[TomlSettings.BACKEND]) else: # fallback to defaukt -> exit ?? LOGGER.error( @@ -210,7 +202,7 @@ class Settings: @property # --filter-customers def filter_customers(self) -> str | None: - if self.__settings[TomlSettings.FILTER_CUSTOMERS] in [IncludeExclude.INCLUDE, IncludeExclude.EXCLUDE]: + if self.__settings[TomlSettings.FILTER_CUSTOMERS] in IncludeExclude.list(): return self.__settings[TomlSettings.FILTER_CUSTOMERS] elif self.__settings[TomlSettings.FILTER_CUSTOMERS] is not None: LOGGER.error( @@ -221,7 +213,7 @@ class Settings: @property # --filter-sites def filter_sites(self) -> str | None: - if self.__settings[TomlSettings.FILTER_SITES] in [IncludeExclude.INCLUDE, IncludeExclude.EXCLUDE]: + if self.__settings[TomlSettings.FILTER_SITES] in IncludeExclude.list(): return self.__settings[TomlSettings.FILTER_SITES] elif self.__settings[TomlSettings.FILTER_SITES] is not None: LOGGER.error( @@ -232,12 +224,12 @@ class Settings: @property # --l2-case def l2_case(self) -> str | None: - if self.__settings[TomlSettings.L2_CASE] in [Case.LOWER, Case.UPPER, Case.INSENSITIVE, Case.AUTO, Case.OFF]: + if self.__settings[TomlSettings.L2_CASE] in L2Case.list(): return self.__settings[TomlSettings.L2_CASE] elif self.__settings[TomlSettings.L2_CASE] is not None: LOGGER.error( f'Unknown case setting {self.__settings[TomlSettings.L2_CASE]}. ' - f'Accepted are {Case.LOWER}|{Case.UPPER}|{Case.INSENSITIVE}|{Case.AUTO}|{Case.OFF}. Falling back to "OFF" (no change).' + f'Accepted are {L2Case.LOWER}|{L2Case.UPPER}|{L2Case.INSENSITIVE}|{L2Case.AUTO}|{L2Case.OFF}. Falling back to "OFF" (no change).' ) return None @@ -249,6 +241,15 @@ class Settings: def l2_display_neighbours(self) -> bool: return bool(self.__settings[TomlSettings.L2_DISPLAY_NEIGHBOURS]) + @property # --l2-ignore-mismatch + def l2_ignore_mismatch(self) -> List[str]: + if self.__l2_ignore_mismatch is None: + self.__l2_ignore_mismatch = list(set(self.__settings[TomlSettings.L2_IGNORE_MISMATCH])) + self.__l2_ignore_mismatch = [entry for entry in self.__l2_ignore_mismatch if entry in L2IgnoreMismatch.list()] + if len(self.__layers) != len(self.__settings[TomlSettings.LAYERS]): + LOGGER.error(f'Wrong/duplicate l2_mismatch_ignore options ignored, {self.__settings[TomlSettings.L2_IGNORE_MISMATCH]}') + return self.__l2_ignore_mismatch + @property # --l2-prefix def l2_prefix(self) -> str: if self.__settings[TomlSettings.L2_PREFIX] is not None: @@ -257,11 +258,11 @@ class Settings: @property # --l2-remove-domain def l2_remove_domain(self) -> str: - if self.__settings[TomlSettings.L2_REMOVE_DOMAIN] in [RemoveDomain.ON, RemoveDomain.OFF, RemoveDomain.AUTO]: + if self.__settings[TomlSettings.L2_REMOVE_DOMAIN] in L2RemoveDomain.list(): return self.__settings[TomlSettings.L2_REMOVE_DOMAIN] else: - self.__settings[TomlSettings.L2_REMOVE_DOMAIN] = RemoveDomain.OFF - return RemoveDomain.OFF + self.__settings[TomlSettings.L2_REMOVE_DOMAIN] = L2RemoveDomain.OFF + return L2RemoveDomain.OFF @property # --l2-skip-external def l2_skip_external(self) -> bool: @@ -301,12 +302,17 @@ class Settings: @property # --layers def layers(self) -> List[str]: - return self.__settings[TomlSettings.LAYERS] + if self.__layers is None: + self.__layers = list(set(self.__settings[TomlSettings.LAYERS])) + self.__layers = [entry for entry in self.__layers if entry in Layers.list()] + if len(self.__layers) != len(self.__settings[TomlSettings.LAYERS]): + LOGGER.error(f'Wrong/duplicate layer(s) ignored, {self.__settings[TomlSettings.LAYERS]}.') + return self.__layers @property # --log-file def log_file(self) -> str: raw_log_file = str(Path(str(self.__settings[TomlSettings.LOG_FILE])).expanduser()) - if not raw_log_file.startswith(f'{OMD_ROOT}/var/log/'): + if not raw_log_file.startswith(f'{LOG_PATH_DEFAULT}/'): # logger not ready yet print(f'\nInvalid log file {raw_log_file}. Falling back to {LOG_FILE_DEFAULT}') return LOG_FILE_DEFAULT @@ -405,13 +411,13 @@ class Settings: return self.__filter_by_site @staticmethod - def parse_key_value_section(section: str, data: Mapping[str, str]) -> Dict[str, set[str]]: + def parse_key_value_section(section: str, data: Mapping[str, str]) -> Mapping[str, set[str]]: parsed = { IncludeExclude.INCLUDE: set(), IncludeExclude.EXCLUDE: set() } for key_value, mode in data.items(): - if mode not in [IncludeExclude.INCLUDE, IncludeExclude.EXCLUDE]: + if mode not in IncludeExclude.list(): LOGGER.error( f'Invalid mode in {section} found: {key_value}={mode} -> line ignored' ) @@ -438,7 +444,7 @@ class Settings: return parsed @property - def filter_by_folder(self) -> Dict[str, set[str]]: + def filter_by_folder(self) -> Mapping[str, set[str]]: if self.__filter_by_folder is None: self.__filter_by_folder = self.parse_key_value_section( section=TomlSections.FILTER_BY_FOLDER, diff --git a/source/bin/nvdct/lib/topologies.py b/source/bin/nvdct/lib/topologies.py index 7a4634f..69a4b4d 100755 --- a/source/bin/nvdct/lib/topologies.py +++ b/source/bin/nvdct/lib/topologies.py @@ -29,6 +29,7 @@ from lib.constants import ( HostLabels, IPVersion, InvPaths, + L2IgnoreMismatch, L2InvColumns, L3InvColumns, LOGGER, @@ -382,6 +383,7 @@ class NvConnections: self, nv_objects: NvObjects, speed_map: Sequence[Thickness], + ignore_mismatch: Sequence[str], ): for connection in self.nv_connections: warning = False @@ -411,7 +413,7 @@ class NvConnections: # left_thickness = map_speed_to_thickness(left_speed, speed_map) metadata['line_config']['thickness'] = right_thickness - if right_speed != left_speed: + if right_speed != left_speed and not L2IgnoreMismatch.SPEED in ignore_mismatch: warning = True metadata = add_tooltip_html( metadata, 'Speed', left, left_speed_str, right, right_speed_str @@ -428,7 +430,7 @@ class NvConnections: # for duplex/native vlan it might be a good idea to change left/right # value as they are reported by CDP as neighbour states if left_duplex and right_duplex: - if left_duplex != right_duplex: + if left_duplex != right_duplex and not L2IgnoreMismatch.DUPLEX in ignore_mismatch: warning = True metadata = add_tooltip_html( @@ -439,7 +441,7 @@ class NvConnections: f'Connection duplex mismatch: {left} ({left_duplex})' f'<->{right} ({right_duplex})' ) - if left_native_vlan and right_native_vlan: + if left_native_vlan and right_native_vlan and not L2IgnoreMismatch.VLAN in ignore_mismatch: if left_native_vlan != '0' and right_native_vlan != '0': # ignore VLAN 0 (Native VLAN on routed ports) if left_native_vlan != right_native_vlan: warning = True @@ -710,7 +712,7 @@ class TopologyL2(Topology): self.hosts_to_go: MutableSet[str] = set(seed_devices) self.label: str = label self.neighbour_replace_regex: List[Tuple[str, str]] = neighbour_replace_regex - self.neighbour_to_host: MutableMapping[str, str] = {} + self.neighbour_to_host: MutableMapping[str, str | None] = {} self.path_in_inventory: str = path_in_inventory self.raw_neighbour_to_neighbour: Dict[str, str] = {} self.skip_external: bool = skip_external @@ -910,7 +912,7 @@ class TopologyL3(Topology): super().__init__( emblems=emblems, host_cache=host_cache, - topology=f'[L3 IPv{version}]' + topology=f'[L3 IPv{version}]', ) self.diplay_devices = display_devices self.ignore_hosts: Sequence[str] = ignore_hosts diff --git a/source/bin/nvdct/lib/update_config.py b/source/bin/nvdct/lib/update_config.py new file mode 100755 index 0000000..b1f52fb --- /dev/null +++ b/source/bin/nvdct/lib/update_config.py @@ -0,0 +1,402 @@ +#!/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 : 202-01-03 +# File : nvdct/lib/adjust_toml.py + + +from pathlib import Path +from re import findall as re_findall, sub as re_sub +from sys import exit as sys_exit + +from lib.constants import ( + API_PORT_DEFAULT, + Backends, + EmblemNames, + EmblemValues, + ExitCodes, + IncludeExclude, + L2Case, + L2IgnoreMismatch, + L2RemoveDomain, + Layers, + LogLevels, + TomlSections, + TomlSettings, +) + + +def update_config(toml_file: str): + fix_options = { + 'DROP_HOSTS': TomlSections.L2_DROP_NEIGHBOURS, + 'HOST_MAP': TomlSections.L2_NEIGHBOUR_TO_HOST_MAP, + 'L2_DROP_HOSTS': TomlSections.L2_DROP_NEIGHBOURS, + 'L2_HOST_MAP': TomlSections.L2_NEIGHBOUR_TO_HOST_MAP, + 'L3V4_IGNORE_HOSTS': TomlSections.L3_IGNORE_HOSTS, + 'L3V4_IGNORE_IP': TomlSections.L3_IGNORE_IP, + 'L3V4_IRNORE_WILDCARD': TomlSections.L3V4_IGNORE_WILDCARD, + 'L3V4_REPLACE': TomlSections.L3_REPLACE_NETWORKS, + 'L3V3_REPLACE': TomlSections.L3_REPLACE_NETWORKS, + 'L3_REPLACE': TomlSections.L3_REPLACE_NETWORKS, + 'L3V4_SUMMARIZE': TomlSections.L3_SUMMARIZE, + 'SEED_DEVICES': TomlSections.L2_SEED_DEVICES, + 'SITES': TomlSections.FILTER_BY_SITE, + 'CUSTOMERS': TomlSections.FILTER_BY_CUSTOMER, + 'icon_missinc': EmblemValues.ICON_ALERT_UNREACHABLE, + 'icon_missing': EmblemValues.ICON_ALERT_UNREACHABLE, + 'l3v4_replace': EmblemNames.L3_REPLACE, + 'l3v4_summarize': EmblemNames.L3_SUMMARIZE, + 'keep_domain = true': f'{TomlSettings.L2_REMOVE_DOMAIN} = "{L2RemoveDomain.OFF}"', + 'keep_domain = false': f'{TomlSettings.L2_REMOVE_DOMAIN} = "{L2RemoveDomain.ON}"', + 'case': TomlSettings.L2_CASE, + 'prefix': TomlSettings.L2_PREFIX, + 'remove_domain': TomlSettings.L2_REMOVE_DOMAIN, + 'keep': TomlSettings.KEEP_MAX_TOPOLOGIES, + 'min_age': TomlSettings.MIN_TOPOLOGY_AGE, + 'display_l2_neighbours': TomlSettings.L2_DISPLAY_NEIGHBOURS, + 'include_l3_hosts': TomlSettings.L3_INCLUDE_HOSTS, + 'include_l3_loopback': TomlSettings.L3_INCLUDE_LOOPBACK, + 'skip_l3_cidr_0': TomlSettings.L3_SKIP_CIDR_0, + 'skip_l3_cidr_32_128': TomlSettings.L3_SKIP_CIDR_32_128, + 'skip_l3_if': TomlSettings.L3_SKIP_IF, + 'skip_l3_ip': TomlSettings.L3_SKIP_IP, + 'skip_l3_public': TomlSettings.L3_SKIP_PUBLIC, + } + + fix_params = { + f'# {TomlSettings.L2_CASE} = "{L2Case.LOWER}" | "{L2Case.UPPER}"\n': f'# {TomlSettings.L2_CASE} = "{L2Case.LOWER}" | "{L2Case.UPPER}" | {L2Case.INSENSITIVE} | "{L2Case.AUTO}" | "{L2Case.OFF}"\n', + f'# {TomlSettings.L2_CASE} = "{L2Case.LOWER}\n"' : f'# {TomlSettings.L2_CASE} = "{L2Case.LOWER}" | "{L2Case.UPPER}" | {L2Case.INSENSITIVE} | "{L2Case.AUTO}" | "{L2Case.OFF}"\n', + f'# {TomlSettings.L2_CASE} = "{L2Case.UPPER}\n"': f'# {TomlSettings.L2_CASE} = "{L2Case.LOWER}" | "{L2Case.UPPER}" | {L2Case.INSENSITIVE} | "{L2Case.AUTO}" | "{L2Case.OFF}\n', + f'# {TomlSettings.L2_REMOVE_DOMAIN} = false | true\n': f'# {TomlSettings.L2_REMOVE_DOMAIN} = "{L2RemoveDomain.OFF}" | "{L2RemoveDomain.ON}" | "{L2RemoveDomain.AUTO}"\n', + f'# {TomlSettings.L2_REMOVE_DOMAIN} = false\n': f'# {TomlSettings.L2_REMOVE_DOMAIN} = "{L2RemoveDomain.OFF}" | "{L2RemoveDomain.ON}" | "{L2RemoveDomain.AUTO}"\n', + f'# {TomlSettings.L2_REMOVE_DOMAIN} = true\n': f'# {TomlSettings.L2_REMOVE_DOMAIN} = "{L2RemoveDomain.OFF}" | "{L2RemoveDomain.ON}" | "{L2RemoveDomain.AUTO}"\n', + f'{TomlSettings.L2_REMOVE_DOMAIN} = false\n': f'{TomlSettings.L2_REMOVE_DOMAIN} = "{L2RemoveDomain.OFF}"\n', + f'{TomlSettings.L2_REMOVE_DOMAIN} = true\n': f'{TomlSettings.L2_REMOVE_DOMAIN} = "{L2RemoveDomain.ON}"\n', + } + + old_options = { + 'CUSTOM_LAYERS': f'Can be removed (no longer supported).', + 'FILESYSTEM': f'Use {Backends.MULTISITE} instead.', + 'debug': f'Use "{TomlSettings.LOG_LEVEL} = {LogLevels.DEBUG}" instead.', + 'lowercase': f'Use "{TomlSettings.L2_CASE} = {L2Case.LOWER}" instead.', + 'uppercase': f'Use "{TomlSettings.L2_CASE} = {L2Case.UPPER}" instead.', + 'CUSTOM': f'Is no loger supported. remove it from layers please.' + } + # sorted in reverse + missing_settings = { + TomlSettings.TIME_FORMAT: '"%Y-%m-%dT%H:%M:%S.%m"', + TomlSettings.QUIET: 'false | true', + TomlSettings.PRE_FETCH: 'false | true', + TomlSettings.OUTPUT_DIRECTORY: '"nvdct" # remove to get date formated directory', + TomlSettings.MIN_TOPOLOGY_AGE: '1', + TomlSettings.LOG_TO_STDOUT: 'false | true', + TomlSettings.LOG_LEVEL: f'"{LogLevels.WARNING}" | "{LogLevels.DEBUG}" | "{LogLevels.INFO}" | "{LogLevels.ERROR}" | "{LogLevels.FATAL}" | {LogLevels.CRITICAL}" | "{LogLevels.OFF}"', + TomlSettings.LOG_FILE: '"~/var/log/nvdct.log"', + TomlSettings.LAYERS: f'["{Layers.CDP}", "{Layers.LLDP}", "{Layers.L3V4}", "{Layers.STATIC}"]', + TomlSettings.L3_SKIP_PUBLIC: 'false | true', + TomlSettings.L3_SKIP_IP: 'false | true', + TomlSettings.L3_SKIP_IF: 'false | true', + TomlSettings.L3_SKIP_CIDR_32_128: 'false | true', + TomlSettings.L3_SKIP_CIDR_0: 'false | true', + TomlSettings.L3_INCLUDE_LOOPBACK: 'false | true # most likely dropped from inventory (SNMP) before', + TomlSettings.L3_INCLUDE_HOSTS: 'false | true', + TomlSettings.L3_DISPLAY_DEVICES: 'false | true', + TomlSettings.L2_SKIP_EXTERNAL: 'false | true', + TomlSettings.L2_REMOVE_DOMAIN: f'"{L2RemoveDomain.OFF}" | "{L2RemoveDomain.ON}" | "{L2RemoveDomain.AUTO}"', + TomlSettings.L2_PREFIX: '""', + TomlSettings.L2_IGNORE_MISMATCH: f'["{L2IgnoreMismatch.DUPLEX}", "{L2IgnoreMismatch.SPEED}", "{L2IgnoreMismatch.VLAN}"]', + TomlSettings.L2_DISPLAY_NEIGHBOURS: 'false | true', + TomlSettings.L2_DISPLAY_PORTS: 'false | true', + TomlSettings.L2_CASE: f'"{L2Case.OFF}" | "{L2Case.LOWER}" | "{L2Case.UPPER}" | {L2Case.INSENSITIVE} | "{L2Case.AUTO}"', + TomlSettings.KEEP_MAX_TOPOLOGIES: '10', + TomlSettings.FILTER_SITES: f'"{IncludeExclude.INCLUDE}" | "{IncludeExclude.EXCLUDE}"', + TomlSettings.FILTER_CUSTOMERS: f'"{IncludeExclude.INCLUDE}" | "{IncludeExclude.EXCLUDE}"', + TomlSettings.DONT_COMPARE: 'false | true', + TomlSettings.DEFAULT: 'false | true', + TomlSettings.BACKEND: f'"{Backends.MULTISITE}" | "{Backends.RESTAPI}" | "{Backends.LIVESTATUS}"', + TomlSettings.API_PORT: API_PORT_DEFAULT, + } + + l2_neighbour_to_host_map = r''' +# map inventory CDP/LLDP neighbour name to Checkmk host name +# [0-9-a-zA-Z\.\_\-]{1,253} -> host + +# "inventory_neighbour1" = "cmk_host1" +# "inventory_neighbour2" = "cmk_host2" +# "inventory_neighbour3" = "cmk_host3" + ''' + + l2_neighbour_replace_regex = r''' +# modify CDP/LLDP neighbour name with regex before mapping to CMK host names + +# "regex string to replace" = "string to replace with" +# "^(([0-9a-fA-F]){2}[:.-]?){5}([0-9a-fA-F]){2}$" = "" +# "\\([0-9a-zA-Z]+\\)$" = "" +# "^Meraki.*\\s-\\s" = "" + ''' + + l3_replace_networks = r''' +# replace _network objects_ in L§ topologies (takes place after summarize) +# [0-9-a-zA-Z\.\_\-]{1,253} -> host + +# "10.193.172.0/24" = "MPLS" +# "10.194.8.0/23" = "MPLS" +# "10.194.12.0/24" = "MPLS" +# "10.194.115.0/24" = "MPLS" +# "fc00::/7" = "Unique-local" + ''' + + emblems = f''' +# can use misc icons from CMK or upload your own in the misc category +# for built-in icons use "icon_" as l2_prefix to the name from CMK +# max size 80x80px +# emblems will only be used for non CMK objects + +# "{EmblemNames.HOST_NODE}" = "{EmblemValues.ICON_ALERT_UNREACHABLE}" +# "{EmblemNames.IP_ADDRESS}" = "{EmblemValues.IP_ADDRESS_80}" +# "{EmblemNames.IP_NETWORK}" = "{EmblemValues.IP_NETWORK_80}" +# "{EmblemNames.L3_REPLACE}" = "{EmblemValues.ICON_PLUGINS_CLOUD}" +# "{EmblemNames.L3_SUMMARIZE}" = "{EmblemValues.ICON_AGGREGATION}" +# "{EmblemNames.SERVICE_NODE}" = "{EmblemValues.ICON_ALERT_UNREACHABLE}" + ''' + + map_speed_to_thickness = r''' +# must be sorted from slower to faster speed +# use only one/no entry to have all connections with the same thickness +# "bits per second" = thickness + +# "2000000" = 1 # 2 mbit +# "5000000" = 2 # 5 mbit +# "1e7" = 3 # 10 mbit +# "51e7" = 4 # 51 mbit +"1e8" = 1 # 100 mbit +"1e9" = 3 # 1 gbit +"1e10" = 5 # 10 gbit + ''' + + filter_by_folder = f''' +# "/folder1/subfolder1" = "{IncludeExclude.INCLUDE}" | "{IncludeExclude.EXCLUDE}" +# "/folder2/subfolder2" = "{IncludeExclude.INCLUDE}" | "{IncludeExclude.EXCLUDE}" +''' + + filter_host_by_label = f''' +# "hostlabel1:value" = "{IncludeExclude.INCLUDE}" | "{IncludeExclude.EXCLUDE}" +# "hostlabel2:value" = "{IncludeExclude.INCLUDE}" | "{IncludeExclude.EXCLUDE}" +''' + + filter_host_by_tag = f''' +# "host_tag1:value" = "{IncludeExclude.INCLUDE}" | "{IncludeExclude.EXCLUDE}" +# "host_tag2:value" = "{IncludeExclude.INCLUDE}" | "{IncludeExclude.EXCLUDE}" + ''' + + missing_tables = { + TomlSections.L2_NEIGHBOUR_TO_HOST_MAP: l2_neighbour_to_host_map, + TomlSections.L2_NEIGHBOUR_REPLACE_REGEX: l2_neighbour_replace_regex, + TomlSections.L3_REPLACE_NETWORKS: l3_replace_networks, + TomlSections.EMBLEMS: emblems, + TomlSections.MAP_SPEED_TO_THICKNESS: map_speed_to_thickness, + TomlSections.FILTER_BY_FOLDER: filter_by_folder, + TomlSections.FILTER_BY_HOST_LABEL: filter_host_by_label, + TomlSections.FILTER_BY_HOST_TAG: filter_host_by_tag, + } + + l2_seed_devices = r''' = [ + # list of CDP/LLDP seed devices (if empty, all CDP/LLDP devices will be used) + # [0-9-a-zA-Z\.\_\-]{1,253} -> host + + # "CORE01", + # "LOCATION01", + # "LOCATION02", +] + ''' + + l2_drop_neighbours = r''' = [ + # drop CDP/LLDP neighbours names + + # "not advertised", + # "a nother invalid name", +] + ''' + + l3_ignore_hosts = r''' = [ + # hosts will be ignored in L3 topologies + # [0-9-a-zA-Z\.\_\-]{1,253} -> host + + # "host1", + # "host2", +] + ''' + + l3_ignore_ip = r''' = [ + # drop IP address that matches ip/network + + # "192.168.100.231", + # "192.168.100.0/16", + # "192.168.150.0/255.255.255.0", + # "fd00::1" + # "fd00::/8" +] + ''' + + l3v4_ignore_wildcard = r''' = [ + # ignore IPs by wildcard + # if comparing an ip address: + # each 0 bit in the wildcard has to be exactly as in the pattern + # each 1 bit in the wildcard will be ignored + + # [ pattern , wildcard ] + # ["172.17.0.1", "0.0.255.0"], # ignore all IPs ending with 1 from 172.17.0.0/16 + # ["172.17.128.0", "0.0.127.3"], # ignore all IPs ending with 0-3 from 172.17.128.0/17 + # ["172.17.128.3", "0.0.127.0"], # ignore all IPs ending with 3 from 172.17.128.0/17 +] +''' + + l3_summarize = r''' = [ + # IP _networks_ to summarize + + # "10.193.172.0/24", + # "10.194.8.0/23", + # "10.194.12.0/24", + # "10.194.115.0/255.255.255.0", + # "fd00::/8" +] + ''' + + protected_topologies = r''' = [ + # topologies will not be deleted by "--keep_max_topologies" + + # "2023-10-17T14:08:05.10", + # "your_important_topology" +] + ''' + + static_connections = r''' = [ + # user defined static connections + # [0-9-a-zA-Z\.\_\-]{1,253} -> host + + # valid entry formats + # ["left_host", "left_service", "right_service", "right_host"], + # connection: "left_host"<->"left_service"<->"right_service"<->"right_host" + # ["left_host", "", "right_service", "right_host"], + # connection: "left_host"<->"right_service"<->"right_host" + # ["left_host", "left_service", "", "right_host"], + # connection: "left_host"<->"left_service"<->"right_host" + # ["left_host", "", "", "right_host"], + # connection: "left_host"<->"right_host" +] + ''' + + filter_by_customer = r''' = [ + # list customers to include/exclude, use with option --filter-costumers INCLUDE/EXCLUDE + # [0-9-a-zA-Z\.\_\-]{1,16} -> customer + + # "customer1", + # "customer2", + # "customer3" +] + ''' + + filter_by_site = r''' = [ + # list site to include/exclude, use with option --filter-sites INCLUDE/EXCLUDE + # [0-9-a-zA-Z\.\_\-]{1,16} -> site + + # "site1", + # "site2", + # "site3", +] + ''' + + missing_arrays = { + TomlSections.FILTER_BY_SITE: filter_by_site, + TomlSections.FILTER_BY_CUSTOMER: filter_by_customer, + TomlSections.STATIC_CONNECTIONS: static_connections, + TomlSections.PROTECTED_TOPOLOGIES: protected_topologies, + TomlSections.L3_SUMMARIZE: l3_summarize, + TomlSections.L3V4_IGNORE_WILDCARD: l3v4_ignore_wildcard, + TomlSections.L3_IGNORE_IP: l3_ignore_ip, + TomlSections.L3_IGNORE_HOSTS: l3_ignore_hosts, + TomlSections.L2_DROP_NEIGHBOURS: l2_drop_neighbours, + TomlSections.L2_SEED_DEVICES: l2_seed_devices, + } + + changed: bool = False + org_file = Path(toml_file) + if not org_file.exists(): + print(f'Config file {org_file.name} not found!') + sys_exit(ExitCodes.FILE_NOT_FOUND) + + print(f'Checking file.: {org_file.name}') + org_content: str = org_file.read_text() + content: str = org_content + for old, new in fix_options.items(): + re_pattern = f'\\b{old}\\b' + count = len(re_findall(re_pattern, content)) + if count > 0: + changed = True + content = re_sub(re_pattern, new, content) + print(f'Found value...: "{old}" {count} times, replaced by "{new}"') + + for old, new in fix_params.items(): + if old in content: + changed = True + content = content.replace(old, new) + new_line = '\n' + print(f'Found value...: "{old.replace(new_line, "")}", replaced by "{new.replace(new_line, "")}"') + + for missing_setting, value in missing_settings.items(): + re_pattern = f'\\b{missing_setting}\\b' + count = len(re_findall(re_pattern, content)) + if count == 0: + changed = True + content = re_sub( + f'\\[{TomlSections.SETTINGS}\\]\n', + f'[{TomlSections.SETTINGS}]\n# {missing_setting} = {value}\n', + content + ) + print(f'Added option..: "# {missing_setting} = {value}"') + + for table, value in missing_tables.items(): + re_pattern = f'\\[{table}\\]\n' + count = len(re_findall(re_pattern, content)) + if count == 0: + changed = True + content = re_sub( + f'\\[{TomlSections.SETTINGS}\\]\n', + f'[{table}]{value}\n[{TomlSections.SETTINGS}]\n', + content + ) + print(f'Added section.: "[{table}]"') + + for array, value in missing_arrays.items(): + re_pattern = f'\\b{array}\\b' + count = len(re_findall(re_pattern, content)) + if count == 0: + changed = True + content = content.replace(']\n\n#', f']\n\n#\n{array}{value}\n#', 1) + print(f'Added section.: "{array} = []"') + pass + + for old, new in old_options.items(): + re_pattern = f'\\b{old}\\b' + count = len(re_findall(re_pattern, org_content)) + if count > 0: + print(f'Obsolete......: "{old}", {new}') + + if changed: + backup_file = Path(f'{toml_file}.backup') + if not backup_file.exists(): + org_file.rename(backup_file) + print(f'Renamed TOML..: {backup_file.name}') + new_file = Path(toml_file) + new_file.open('w').write(content) + print(f'Written fixed.: {new_file.name}') + else: + print( + f'Can not create backup file {backup_file.name}, file exists. Aborting!\n' + f'Nothing has changed.' + ) + else: + print('Finished......: Nothing found to fix.') diff --git a/source/bin/nvdct/lib/utils.py b/source/bin/nvdct/lib/utils.py index 4eb62ee..98878f4 100755 --- a/source/bin/nvdct/lib/utils.py +++ b/source/bin/nvdct/lib/utils.py @@ -237,7 +237,6 @@ def is_valid_output_directory(directory: str) -> bool: re_host_pattern = r'^[0-9a-z-A-Z\.\-\_\:]{1,30}$' if re_match(re_host_pattern, directory): return True - return True else: LOGGER.error(f'Invalid output directory name found: {directory}') return False diff --git a/source/bin/nvdct/nvdct.py b/source/bin/nvdct/nvdct.py index 48d4716..2e59506 100755 --- a/source/bin/nvdct/nvdct.py +++ b/source/bin/nvdct/nvdct.py @@ -221,6 +221,7 @@ # fixed REST API query for interface services # 2025-01-24: added option --l2-display-ports, --l3-display-devices # 2025-02-05: added option "OFF" to --l2-case +# 2025-02-07: added option --l2-ignore-mismatch # # creating topology data json from inventory data # @@ -541,6 +542,7 @@ def main(): topology.nv_connections.add_meta_data_to_connections( nv_objects=topology.nv_objects, speed_map=settings.map_speed_to_thickness, + ignore_mismatch=settings.l2_ignore_mismatch, ) topology.save( diff --git a/source/packages/nvdct b/source/packages/nvdct index d277e55..5c37104 100644 --- a/source/packages/nvdct +++ b/source/packages/nvdct @@ -40,14 +40,15 @@ 'nvdct/lib/__init__.py', 'nvdct/conf/nvdct.toml', 'nvdct/lib/topologies.py', - 'nvdct/lib/constants.py'], + 'nvdct/lib/constants.py', + 'nvdct/lib/update_config.py'], 'web': ['htdocs/images/icons/cloud_80.png', 'htdocs/images/icons/ip-address_80.png', 'htdocs/images/icons/ip-network_80.png', 'htdocs/images/icons/location_80.png']}, 'name': 'nvdct', 'title': 'Network Visualization Data Creation Tool (NVDCT)', - 'version': '0.9.8-20250205', + 'version': '0.9.9-20250214', 'version.min_required': '2.3.0b1', 'version.packaged': 'cmk-mkp-tool 0.2.0', 'version.usable_until': '2.4.0p1'} -- GitLab