From 15195998057e24339dd751fa4b96f1596cac3dee Mon Sep 17 00:00:00 2001 From: "ricardo.bartels@telekom.de" Date: Thu, 9 Feb 2023 22:48:15 +0100 Subject: [PATCH] WIP: adds permitted subnet class and finishes redfish import --- module/common/config.py | 63 +++--- module/config/base.py | 6 +- module/netbox/config.py | 191 +++++++++--------- module/sources/check_redfish/config.py | 104 ++++++---- .../sources/check_redfish/import_inventory.py | 88 +------- module/sources/common/conifg.py | 25 +++ module/sources/common/permitted_subnets.py | 113 +++++++++++ module/sources/common/source_base.py | 16 -- module/sources/vmware/config.py | 106 +++++----- module/sources/vmware/connection.py | 9 +- netbox-sync.py | 3 +- 11 files changed, 408 insertions(+), 316 deletions(-) create mode 100644 module/sources/common/conifg.py create mode 100644 module/sources/common/permitted_subnets.py diff --git a/module/common/config.py b/module/common/config.py index 0b1a445..274346d 100644 --- a/module/common/config.py +++ b/module/common/config.py @@ -20,35 +20,38 @@ class CommonConfig(ConfigBase): section_name = common_config_section_name - options = [ - ConfigOption("log_level", - str, - description="""\ - Logs will always be printed to stdout/stderr. - Logging can be set to following log levels: - ERROR: Fatal Errors which stops regular a run - WARNING: Warning messages won't stop the syncing process but mostly worth - to have a look at. - INFO: Information about objects that will be create/updated/deleted in NetBox - DEBUG: Will log information about retrieved information, changes in internal - content structure and parsed config - DEBUG2: Will also log information about how/why content is parsed or skipped. - DEBUG3: Logs all source and NetBox queries/results to stdout. Very useful for - troubleshooting, but will log any sensitive content contained within a query. - """, - default_value="INFO"), + def __init__(self): + self.options = [ + ConfigOption("log_level", + str, + description="""\ + Logs will always be printed to stdout/stderr. + Logging can be set to following log levels: + ERROR: Fatal Errors which stops regular a run + WARNING: Warning messages won't stop the syncing process but mostly worth + to have a look at. + INFO: Information about objects that will be create/updated/deleted in NetBox + DEBUG: Will log information about retrieved information, changes in internal + content structure and parsed config + DEBUG2: Will also log information about how/why content is parsed or skipped. + DEBUG3: Logs all source and NetBox queries/results to stdout. Very useful for + troubleshooting, but will log any sensitive content contained within a query. + """, + default_value="INFO"), - ConfigOption("log_to_file", - bool, - description="""Enabling this options will write all - logs to a log file defined in 'log_file' - """, - default_value=True), + ConfigOption("log_to_file", + bool, + description="""Enabling this options will write all + logs to a log file defined in 'log_file' + """, + default_value=True), - ConfigOption("log_file", - str, - description="""Destination of the log file if "log_to_file" is enabled. - Log file will be rotated maximum 5 times once the log file reaches size of 10 MB - """, - default_value="log/netbox_sync.log") - ] + ConfigOption("log_file", + str, + description="""Destination of the log file if "log_to_file" is enabled. + Log file will be rotated maximum 5 times once the log file reaches size of 10 MB + """, + default_value="log/netbox_sync.log") + ] + + super().__init__() diff --git a/module/config/base.py b/module/config/base.py index 62daa99..06a5db1 100644 --- a/module/config/base.py +++ b/module/config/base.py @@ -36,6 +36,9 @@ class ConfigBase: def validate_options(self): pass + def set_validation_failed(self): + self._parsing_failed = True + def parse(self, do_log: bool = True): def _log(handler, message): @@ -113,7 +116,8 @@ class ConfigBase: options[config_object.key] = config_object.value - config_options = get_value() or dict() + # check for unknown config options + config_options = get_value() if not isinstance(config_options, dict): config_options = dict() diff --git a/module/netbox/config.py b/module/netbox/config.py index 485621b..b23d2e0 100644 --- a/module/netbox/config.py +++ b/module/netbox/config.py @@ -22,114 +22,117 @@ class NetBoxConfig(ConfigBase): section_name = netbox_config_section_name - options = [ - ConfigOption("api_token", - str, - description="""Requires an NetBox API token with full permissions on all objects except - 'auth', 'secrets' and 'users' - """, - config_example="XYZ", - mandatory=True, - sensitive=True), + def __init__(self): + self.options = [ + ConfigOption("api_token", + str, + description="""Requires an NetBox API token with full permissions on all objects except + 'auth', 'secrets' and 'users' + """, + config_example="XYZ", + mandatory=True, + sensitive=True), - ConfigOption("host_fqdn", - str, - description="Requires a hostname or IP which points to your NetBox instance", - config_example="netbox.example.com", - mandatory=True), + ConfigOption("host_fqdn", + str, + description="Requires a hostname or IP which points to your NetBox instance", + config_example="netbox.example.com", + mandatory=True), - ConfigOption("port", - int, - description="""Define the port your NetBox instance is listening on. - If 'disable_tls' is set to "true" this option might be set to 80 - """, - default_value=443), + ConfigOption("port", + int, + description="""Define the port your NetBox instance is listening on. + If 'disable_tls' is set to "true" this option might be set to 80 + """, + default_value=443), - ConfigOption("disable_tls", - bool, - description="Whether TLS encryption is enabled or disabled", - default_value=False), + ConfigOption("disable_tls", + bool, + description="Whether TLS encryption is enabled or disabled", + default_value=False), - ConfigOption("validate_tls_certs", - bool, - description="""Enforces TLS certificate validation. If this system doesn't trust the NetBox - web server certificate then this option needs to be changed - """, - default_value=True), + ConfigOption("validate_tls_certs", + bool, + description="""Enforces TLS certificate validation. If this system doesn't trust the NetBox + web server certificate then this option needs to be changed + """, + default_value=True), - ConfigOption("proxy", - str, - description="""Defines a proxy which will be used to connect to NetBox. - Proxy setting needs to include the schema. - Proxy basic auth example: http://user:pass@10.10.1.10:312 - """, - config_example="http://example.com:3128"), + ConfigOption("proxy", + str, + description="""Defines a proxy which will be used to connect to NetBox. + Proxy setting needs to include the schema. + Proxy basic auth example: http://user:pass@10.10.1.10:312 + """, + config_example="http://example.com:3128"), - ConfigOption("client_cert", - str, - description="Specify a client certificate which can be used to authenticate to NetBox", - config_example="client.pem"), + ConfigOption("client_cert", + str, + description="Specify a client certificate which can be used to authenticate to NetBox", + config_example="client.pem"), - ConfigOption("client_cert_key", - str, - description="Specify the client certificate private key belonging to the client cert", - config_example="client.key"), + ConfigOption("client_cert_key", + str, + description="Specify the client certificate private key belonging to the client cert", + config_example="client.key"), - ConfigOption("prune_enabled", - bool, - description="""Whether items which were created by this program but - can't be found in any source anymore will be deleted or not - """, - default_value=False), + ConfigOption("prune_enabled", + bool, + description="""Whether items which were created by this program but + can't be found in any source anymore will be deleted or not + """, + default_value=False), - ConfigOption("prune_delay_in_days", - int, - description="""Orphaned objects will first be tagged before they get deleted. - Once the amount of days passed the object will actually be deleted - """, - default_value=30), + ConfigOption("prune_delay_in_days", + int, + description="""Orphaned objects will first be tagged before they get deleted. + Once the amount of days passed the object will actually be deleted + """, + default_value=30), - ConfigOption("ignore_unknown_source_object_pruning", - bool, - description="""This will tell netbox-sync to ignore objects in NetBox - with tag 'NetBox-synced' from pruning if the source is not defined in - this config file (https://github.com/bb-Ricardo/netbox-sync/issues/176) - """, - default_value=False), + ConfigOption("ignore_unknown_source_object_pruning", + bool, + description="""This will tell netbox-sync to ignore objects in NetBox + with tag 'NetBox-synced' from pruning if the source is not defined in + this config file (https://github.com/bb-Ricardo/netbox-sync/issues/176) + """, + default_value=False), - ConfigOption("default_netbox_result_limit", - int, - description="""The maximum number of objects returned in a single request. - If a NetBox instance is very quick responding the value should be raised - """, - default_value=200), + ConfigOption("default_netbox_result_limit", + int, + description="""The maximum number of objects returned in a single request. + If a NetBox instance is very quick responding the value should be raised + """, + default_value=200), - ConfigOption("timeout", - int, - description="""The maximum time a query is allowed to execute before being - killed and considered failed - """, - default_value=30), + ConfigOption("timeout", + int, + description="""The maximum time a query is allowed to execute before being + killed and considered failed + """, + default_value=30), - ConfigOption("max_retry_attempts", - int, - description="""The amount of times a failed request will be reissued. - Once the maximum is reached the syncing process will be stopped completely. - """, - default_value=4), + ConfigOption("max_retry_attempts", + int, + description="""The amount of times a failed request will be reissued. + Once the maximum is reached the syncing process will be stopped completely. + """, + default_value=4), - ConfigOption("use_caching", - bool, - description="""Defines if caching of NetBox objects is used or not. - If problems with unresolved dependencies occur, switching off caching might help. - """, - default_value=True), + ConfigOption("use_caching", + bool, + description="""Defines if caching of NetBox objects is used or not. + If problems with unresolved dependencies occur, switching off caching might help. + """, + default_value=True), - ConfigOption("cache_directory_location", - str, - description="The location of the directory where the cache files should be stored", - default_value="cache") - ] + ConfigOption("cache_directory_location", + str, + description="The location of the directory where the cache files should be stored", + default_value="cache") + ] + + super().__init__() def validate_options(self): @@ -140,4 +143,4 @@ class NetBoxConfig(ConfigBase): (not option.value.startswith("http") and not option.value.startswith("socks5")): log.error(f"Config option 'proxy' in '{NetBoxConfig.section_name}' must contain the schema " f"http, https, socks5 or socks5h") - self._parsing_failed = True + self.set_validation_failed() diff --git a/module/sources/check_redfish/config.py b/module/sources/check_redfish/config.py index d7c8b29..9202c8e 100644 --- a/module/sources/check_redfish/config.py +++ b/module/sources/check_redfish/config.py @@ -7,10 +7,16 @@ # For a copy, see file LICENSE.txt included in this # repository or visit: . +import os + from module.config import source_config_section_name from module.config.base import ConfigBase from module.config.option import ConfigOption -from module.sources.common.source_base import config_option_enabled, config_option_permitted_subnets +from module.sources.common.conifg import * +from module.common.logging import get_logger +from module.sources.common.permitted_subnets import PermittedSubnets + +log = get_logger() class CheckRedfishConfig(ConfigBase): @@ -18,49 +24,69 @@ class CheckRedfishConfig(ConfigBase): section_name = source_config_section_name source_name = None - options = [ - config_option_enabled, + def __init__(self): + self.options = [ + ConfigOption(**config_option_enabled_definition), - ConfigOption("type", - str, - description="type of source. This defines which source handler to use", - config_example="check_redfish", - mandatory=True), + ConfigOption("type", + str, + description="type of source. This defines which source handler to use", + config_example="check_redfish", + mandatory=True), - ConfigOption("inventory_file_path", - str, - description="define the full path where the check_redfish inventory json files are located", - mandatory=True), + ConfigOption("inventory_file_path", + str, + description="define the full path where the check_redfish inventory json files are located", + mandatory=True), - config_option_permitted_subnets, + ConfigOption(**config_option_permitted_subnets_definition), - ConfigOption("overwrite_host_name", - bool, - description="""define if the host name discovered via check_redfish - overwrites the device host name in NetBox""", - default_value=False), + ConfigOption("overwrite_host_name", + bool, + description="""define if the host name discovered via check_redfish + overwrites the device host name in NetBox""", + default_value=False), - ConfigOption("overwrite_power_supply_name", - bool, - description="""define if the name of the power supply discovered via check_redfish - overwrites the power supply name in NetBox""", - default_value=False), + ConfigOption("overwrite_power_supply_name", + bool, + description="""define if the name of the power supply discovered via check_redfish + overwrites the power supply name in NetBox""", + default_value=False), - ConfigOption("overwrite_power_supply_attributes", - bool, - description="""define if existing power supply attributes are overwritten with data discovered - via check_redfish if False only data which is not preset in NetBox will be added""", - default_value=True), + ConfigOption("overwrite_power_supply_attributes", + bool, + description="""define if existing power supply attributes are overwritten with data discovered + via check_redfish if False only data which is not preset in NetBox will be added""", + default_value=True), - ConfigOption("overwrite_interface_name", - bool, - description="""define if the name of the interface discovered via check_redfish - overwrites the interface name in NetBox""", - default_value=False), + ConfigOption("overwrite_interface_name", + bool, + description="""define if the name of the interface discovered via check_redfish + overwrites the interface name in NetBox""", + default_value=False), - ConfigOption("overwrite_interface_attributes", - bool, - description="""define if existing interface attributes are overwritten with data discovered - via check_redfish if False only data which is not preset in NetBox will be added""", - default_value=True) - ] + ConfigOption("overwrite_interface_attributes", + bool, + description="""define if existing interface attributes are overwritten with data discovered + via check_redfish if False only data which is not preset in NetBox will be added""", + default_value=True) + ] + + super().__init__() + + def validate_options(self): + + for option in self.options: + + if option.key == "inventory_file_path": + if not os.path.exists(option.value): + log.error(f"Inventory file path '{option.value}' not found.") + self.set_validation_failed() + + if os.path.isfile(option.value): + log.error(f"Inventory file path '{option.value}' needs to be a directory.") + self.set_validation_failed() + + if not os.access(option.value, os.X_OK | os.R_OK): + log.error(f"Inventory file path '{option.value}' not readable.") + self.set_validation_failed() diff --git a/module/sources/check_redfish/import_inventory.py b/module/sources/check_redfish/import_inventory.py index 0368da2..d04c4fe 100644 --- a/module/sources/check_redfish/import_inventory.py +++ b/module/sources/check_redfish/import_inventory.py @@ -7,7 +7,6 @@ # For a copy, see file LICENSE.txt included in this # repository or visit: . -from ipaddress import ip_network import os import glob import json @@ -18,8 +17,9 @@ from module.sources.common.source_base import SourceBase from module.sources.check_redfish.config import CheckRedfishConfig from module.common.logging import get_logger from module.common.misc import grab, get_string_or_none -from module.common.support import normalize_mac_address, ip_valid_to_add_to_netbox +from module.common.support import normalize_mac_address from module.netbox.inventory import NetBoxInventory +from module.sources.common.permitted_subnets import PermittedSubnets from module.netbox.object_classes import ( NetBoxInterfaceType, NBTag, @@ -103,81 +103,15 @@ class CheckRedfish(SourceBase): log.info(f"Source '{name}' is currently disabled. Skipping") return + self.permitted_subnets = PermittedSubnets(self.settings.permitted_subnets) + if self.permitted_subnets.validation_failed is True: + log.error(f"Config parsing for source '{self.name}' failed.") + return + self.init_successful = True self.interface_adapter_type_dict = dict() - def parse_config_settings(self, config_settings): - """ - Validate parsed settings from config file - - Parameters - ---------- - config_settings: dict - dict of config settings - - """ - - validation_failed = False - - for setting in ["inventory_file_path"]: - if config_settings.get(setting) is None: - log.error(f"Config option '{setting}' in 'source/{self.name}' can't be empty/undefined") - validation_failed = True - - inv_path = config_settings.get("inventory_file_path") - if not os.path.exists(inv_path): - log.error(f"Inventory file path '{inv_path}' not found.") - validation_failed = True - - if os.path.isfile(inv_path): - log.error(f"Inventory file path '{inv_path}' needs to be a directory.") - validation_failed = True - - if not os.access(inv_path, os.X_OK | os.R_OK): - log.error(f"Inventory file path '{inv_path}' not readable.") - validation_failed = True - - # check permitted ip subnets - permitted_subnets = list() - excluded_subnets = list() - self.settings["excluded_subnets"] = excluded_subnets - - if config_settings.get("permitted_subnets") is None: - log.info(f"Config option 'permitted_subnets' in 'source/{self.name}' is undefined. " - f"No IP addresses will be populated to NetBox!") - else: - config_settings["permitted_subnets"] = \ - [x.strip() for x in config_settings.get("permitted_subnets").split(",") if x.strip() != ""] - - # add "invisible" config option - self.settings["excluded_subnets"] = None - - for subnet in config_settings["permitted_subnets"]: - excluded = False - if subnet[0] == "!": - excluded = True - subnet = subnet[1:].strip() - - try: - if excluded is True: - excluded_subnets.append(ip_network(subnet)) - else: - permitted_subnets.append(ip_network(subnet)) - except Exception as e: - log.error(f"Problem parsing permitted subnet: {e}") - validation_failed = True - - config_settings["permitted_subnets"] = permitted_subnets - config_settings["excluded_subnets"] = excluded_subnets - - if validation_failed is True: - log.error("Config validation failed. Exit!") - exit(1) - - for setting in self.settings.keys(): - setattr(self, setting, config_settings.get(setting)) - def apply(self): """ Main source handler method. This method is called for each source from "main" program @@ -870,17 +804,13 @@ class CheckRedfish(SourceBase): # collect ip addresses nic_ips[port_name] = list() for ipv4_address in grab(nic_port, "ipv4_addresses", fallback=list()): - if ip_valid_to_add_to_netbox(ipv4_address, self.settings.permitted_subnets, - excluded_subnets=self.settings.excluded_subnets, - interface_name=port_name) is False: + if self.permitted_subnets.permitted(ipv4_address, interface_name=port_name) is False: continue nic_ips[port_name].append(ipv4_address) for ipv6_address in grab(nic_port, "ipv6_addresses", fallback=list()): - if ip_valid_to_add_to_netbox(ipv6_address, self.settings.permitted_subnets, - excluded_subnets=self.settings.excluded_subnets, - interface_name=port_name) is False: + if self.permitted_subnets.permitted(ipv6_address, interface_name=port_name) is False: continue nic_ips[port_name].append(ipv6_address) diff --git a/module/sources/common/conifg.py b/module/sources/common/conifg.py new file mode 100644 index 0000000..86020c0 --- /dev/null +++ b/module/sources/common/conifg.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020 - 2022 Ricardo Bartels. All rights reserved. +# +# netbox-sync.py +# +# This work is licensed under the terms of the MIT license. +# For a copy, see file LICENSE.txt included in this +# repository or visit: . + +config_option_enabled_definition = { + "key": "enabled", + "value_type": bool, + "description": "Defines if this source is enabled or not", + "default_value": True +} + +config_option_permitted_subnets_definition = { + "key": "permitted_subnets", + "value_type": str, + "description": """IP networks eligible to be synced to NetBox. If an IP address is not part of + this networks then it WON'T be synced to NetBox. To excluded small blocks from bigger IP blocks + a leading '!' has to be added + """, + "config_example": "10.0.0.0/8, !10.23.42.0/24" +} diff --git a/module/sources/common/permitted_subnets.py b/module/sources/common/permitted_subnets.py new file mode 100644 index 0000000..dc08c8c --- /dev/null +++ b/module/sources/common/permitted_subnets.py @@ -0,0 +1,113 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020 - 2022 Ricardo Bartels. All rights reserved. +# +# netbox-sync.py +# +# This work is licensed under the terms of the MIT license. +# For a copy, see file LICENSE.txt included in this +# repository or visit: . + +from ipaddress import ip_address, ip_network, ip_interface + +from module.common.logging import get_logger + +log = get_logger() + + +class PermittedSubnets: + """ + initializes and verifies if an IP address is part of an permitted subnet + """ + + def __init__(self, config_string: str): + + self._validation_failed = False + + self.included_subnets = list() + self.excluded_subnets = list() + + if config_string is None: + log.info(f"Config option 'permitted_subnets' is undefined. No IP addresses will be populated to NetBox!") + self._validation_failed = True + + if not isinstance(config_string, str): + raise ValueError("permitted subnets need to be of type string") + + subnet_list = [x.strip() for x in config_string.split(",") if x.strip() != ""] + + for subnet in subnet_list: + excluded = False + if subnet[0] == "!": + excluded = True + subnet = subnet[1:].strip() + + try: + if excluded is True: + self.excluded_subnets.append(ip_network(subnet)) + else: + self.included_subnets.append(ip_network(subnet)) + except Exception as e: + log.error(f"Problem parsing permitted subnet: {e}") + self._validation_failed = True + + @property + def validation_failed(self) -> bool: + return self._validation_failed + + def permitted(self, ip, interface_name=None) -> bool: + """ + performs a couple of checks to see if an IP address is valid and allowed + to be added to NetBox + + IP address must always be passed as interface notation + * 192.168.0.1/24 + * fd00::0/64 + * 192.168.23.24/255.255.255.24 + + Parameters + ---------- + ip: str + IP address to validate + interface_name: str + name of the interface this IP shall be added. Important for meaningful log messages + + Returns + ------- + bool: if IP address is valid + """ + + if ip is None: + log.warning("No IP address passed to validate if this IP belongs to a permitted subnet") + return False + + ip_text = f"'{ip}'" + if interface_name is not None: + ip_text = f"{ip_text} for {interface_name}" + + try: + if "/" in ip: + ip_a = ip_interface(ip).ip + else: + ip_a = ip_address(ip) + except ValueError: + log.error(f"IP address {ip_text} invalid!") + return False + + if ip_a.is_link_local is True: + log.debug(f"IP address {ip_text} is a link local address. Skipping.") + return False + + if ip_a.is_loopback is True: + log.debug(f"IP address {ip_text} is a loopback address. Skipping.") + return False + + for excluded_subnet in self.excluded_subnets: + if ip_a in excluded_subnet: + return False + + for permitted_subnet in self.included_subnets: + if ip_a in permitted_subnet: + return True + + log.debug(f"IP address {ip_text} not part of any permitted subnet. Skipping.") + return False diff --git a/module/sources/common/source_base.py b/module/sources/common/source_base.py index 547e4f5..ffb49c1 100644 --- a/module/sources/common/source_base.py +++ b/module/sources/common/source_base.py @@ -25,25 +25,9 @@ from module.netbox.inventory import ( ) from module.common.logging import get_logger from module.common.misc import grab -from module.config.option import ConfigOption log = get_logger() -config_option_enabled = \ - ConfigOption("enabled", - bool, - description="Defines if this source is enabled or not", - default_value=True) - -config_option_permitted_subnets = \ - ConfigOption("permitted_subnets", - str, - description="""IP networks eligible to be synced to NetBox. If an IP address is not part of - this networks then it WON'T be synced to NetBox. To excluded small blocks from bigger IP blocks - a leading '!' has to be added - """, - config_example="10.0.0.0/8, !10.23.42.0/24") - class SourceBase: """ diff --git a/module/sources/vmware/config.py b/module/sources/vmware/config.py index 326db08..cd2d956 100644 --- a/module/sources/vmware/config.py +++ b/module/sources/vmware/config.py @@ -10,7 +10,7 @@ from module.config import source_config_section_name from module.config.base import ConfigBase from module.config.option import ConfigOption -from module.sources.common.source_base import config_option_enabled, config_option_permitted_subnets +from module.sources.common.conifg import * class VMWareConfig(ConfigBase): @@ -18,58 +18,70 @@ class VMWareConfig(ConfigBase): section_name = source_config_section_name source_name = None - options = [ - config_option_enabled, + def __init__(self): + self.options = [ + ConfigOption(**config_option_enabled_definition), - ConfigOption("type", - str, - description="type of source. This defines which source handler to use", - config_example="vmware", - mandatory=True), + ConfigOption("type", + str, + description="type of source. This defines which source handler to use", + config_example="vmware", + mandatory=True), - ConfigOption("host_fqdn", - str, - description="host name / IP address of the vCenter", - config_example="my-netbox.local", - mandatory=True), + ConfigOption("host_fqdn", + str, + description="host name / IP address of the vCenter", + config_example="my-netbox.local", + mandatory=True), - ConfigOption("port", - int, - description="TCP port to connect to", - default_value=443, - mandatory=True), + ConfigOption("port", + int, + description="TCP port to connect to", + default_value=443, + mandatory=True), - ConfigOption("username", - str, - description="username to use to log into vCenter", - config_example="vcenter-admin", - mandatory=True), + ConfigOption("username", + str, + description="username to use to log into vCenter", + config_example="vcenter-admin", + mandatory=True), - ConfigOption("password", - str, - description="password to use to log into vCenter", - config_example="super-secret", - sensitive=True, - mandatory=True), + ConfigOption("password", + str, + description="password to use to log into vCenter", + config_example="super-secret", + sensitive=True, + mandatory=True), - ConfigOption("validate_tls_certs", - bool, - description="""Enforces TLS certificate validation. - If vCenter uses a valid TLS certificate then this option should be set - to 'true' to ensure a secure connection."""), + ConfigOption("validate_tls_certs", + bool, + description="""Enforces TLS certificate validation. + If vCenter uses a valid TLS certificate then this option should be set + to 'true' to ensure a secure connection."""), - ConfigOption("proxy_host", - str, - description="""EXPERIMENTAL: Connect to a vCenter using a proxy server - (socks proxies are not supported). define a host name or an IP address""", - config_example="10.10.1.10"), + ConfigOption("proxy_host", + str, + description="""EXPERIMENTAL: Connect to a vCenter using a proxy server + (socks proxies are not supported). define a host name or an IP address""", + config_example="10.10.1.10"), - ConfigOption("proxy_port", - int, - description="""EXPERIMENTAL: Connect to a vCenter using a proxy server - (socks proxies are not supported). - define proxy server port number""", - config_example=3128), + ConfigOption("proxy_port", + int, + description="""EXPERIMENTAL: Connect to a vCenter using a proxy server + (socks proxies are not supported). + define proxy server port number""", + config_example=3128), - config_option_permitted_subnets - ] + ConfigOption(**config_option_permitted_subnets_definition), + + ConfigOption("cluster_exclude_filter", + str), + ] + + super().__init__() + + def validate_options(self): + + for option in self.options: + + pass diff --git a/module/sources/vmware/connection.py b/module/sources/vmware/connection.py index 0b4355d..f3bee3a 100644 --- a/module/sources/vmware/connection.py +++ b/module/sources/vmware/connection.py @@ -31,6 +31,7 @@ from module.common.logging import get_logger, DEBUG3 from module.common.misc import grab, dump, get_string_or_none, plural, quoted_split from module.common.support import normalize_mac_address, ip_valid_to_add_to_netbox from module.netbox.inventory import NetBoxInventory +from module.sources.common.permitted_subnets import PermittedSubnets from module.netbox.object_classes import ( NetBoxInterfaceType, NBTag, @@ -95,14 +96,6 @@ class VMWareHandler(SourceBase): ] settings = { - "enabled": True, - "host_fqdn": None, - "port": 443, - "username": None, - "password": None, - "validate_tls_certs": False, - "proxy_host": None, - "proxy_port": None, "cluster_exclude_filter": None, "cluster_include_filter": None, "host_exclude_filter": None, diff --git a/netbox-sync.py b/netbox-sync.py index bebef51..b84b5a6 100755 --- a/netbox-sync.py +++ b/netbox-sync.py @@ -78,7 +78,7 @@ def main(): inventory = NetBoxInventory() # establish NetBox connection - #nb_handler = NetBoxHandler(nb_sync_version=__version__) + nb_handler = NetBoxHandler(nb_sync_version=__version__) # if purge was selected we go ahead and remove all items which were managed by this tools if args.purge is True: @@ -94,7 +94,6 @@ def main(): # instantiate source handlers and get attributes log.info("Initializing sources") sources = instantiate_sources() - exit(0) # all sources are unavailable if len(sources) == 0: