From 6541cde47eec99b28fb16a50ffda77c68c19f921 Mon Sep 17 00:00:00 2001 From: "ricardo.bartels@telekom.de" Date: Sun, 27 Nov 2022 15:19:04 +0100 Subject: [PATCH] fixes issues with long running jobs and closing connection errors #248 --- module/common/misc.py | 20 ++++++++------ module/netbox/connection.py | 8 ++++++ .../sources/check_redfish/import_inventory.py | 4 +-- module/sources/common/source_base.py | 4 +++ module/sources/vmware/connection.py | 26 ++++++++++++++----- netbox-sync.py | 8 ++++++ 6 files changed, 54 insertions(+), 16 deletions(-) diff --git a/module/common/misc.py b/module/common/misc.py index 43ff6dd..85a3f55 100644 --- a/module/common/misc.py +++ b/module/common/misc.py @@ -124,6 +124,7 @@ def do_error_exit(log_text): def get_relative_time(delta): """ + https://stackoverflow.com/a/13756038 return a human readable string of a datetime object delta Parameters @@ -136,17 +137,20 @@ def get_relative_time(delta): str: formatted string of time delta """ - parts = [float(x) for x in str(delta).split(":")] - - hour, minute, second = "{:1.0f}:{:1.0f}:{:1.2f}".format(*parts).split(":") + seconds = int(delta.total_seconds()) return_string = list() - if hour != "0": - return_string.append(f"{hour} hour%s" % plural(int(hour))) - if minute != "0": - return_string.append(f"{minute} minute%s" % plural(int(minute))) + periods = [ + ('day', 60 * 60 * 24), + ('hour', 60 * 60), + ('minute', 60), + ('second', 1) + ] - return_string.append(f"{second} seconds") + for period_name, period_seconds in periods: + if seconds >= period_seconds: + period_value, seconds = divmod(seconds, period_seconds) + return_string.append(f"{period_value} {period_name}{plural(period_value)}") return ", ".join(return_string) diff --git a/module/netbox/connection.py b/module/netbox/connection.py index f28e741..b832430 100644 --- a/module/netbox/connection.py +++ b/module/netbox/connection.py @@ -234,6 +234,14 @@ class NetBoxHandler: return session + def finish(self): + + # closing NetBox connection + try: + self.session.close() + except Exception as e: + log.error(f"unable to close NetBox connection: {e}") + def get_api_version(self): """ Perform a basic GET request to extract NetBox API version from header diff --git a/module/sources/check_redfish/import_inventory.py b/module/sources/check_redfish/import_inventory.py index 9fa718e..a77638a 100644 --- a/module/sources/check_redfish/import_inventory.py +++ b/module/sources/check_redfish/import_inventory.py @@ -211,8 +211,8 @@ class CheckRedfish(SourceBase): # parse inventory id to int as all NetBox ids are type integer try: inventory_id = int(inventory_id) - except ValueError: - log.error(f"Value for meta.inventory_id '{inventory_id}' must be an integer." + except (ValueError, TypeError): + log.error(f"Value for meta.inventory_id '{inventory_id}' must be an integer. " f"Cannot use inventory_id to match device in NetBox.") self.device_object = self.inventory.get_by_id(NBDevice, inventory_id) diff --git a/module/sources/common/source_base.py b/module/sources/common/source_base.py index c4adb48..54e9953 100644 --- a/module/sources/common/source_base.py +++ b/module/sources/common/source_base.py @@ -37,6 +37,10 @@ class SourceBase: inventory = None source_tag = None + # dummy function to implement a finish call for each source + def finish(self): + pass + def map_object_interfaces_to_current_interfaces(self, device_vm_object, interface_data_dict=None): """ Try to match current object interfaces to discovered ones. This will be done diff --git a/module/sources/vmware/connection.py b/module/sources/vmware/connection.py index 3cfecb0..2ac15d0 100644 --- a/module/sources/vmware/connection.py +++ b/module/sources/vmware/connection.py @@ -7,7 +7,6 @@ # For a copy, see file LICENSE.txt included in this # repository or visit: . -import atexit import datetime import pprint import re @@ -185,6 +184,7 @@ class VMWareHandler(SourceBase): log.info(f"Source '{name}' is currently disabled. Skipping") return + self._sdk_instance = None self.create_sdk_session() if self.session is None: @@ -412,15 +412,14 @@ class VMWareHandler(SourceBase): try: if self.proxy_host is not None and self.proxy_port is not None: smart_stub = connect.SmartStubAdapter(**connection_params) - instance = vim.ServiceInstance('ServiceInstance', smart_stub) - content = instance.RetrieveContent() + self._sdk_instance = vim.ServiceInstance('ServiceInstance', smart_stub) + content = self._sdk_instance.RetrieveContent() content.sessionManager.Login(self.username, self.password, None) else: - instance = connect.SmartConnect(**connection_params) + self._sdk_instance = connect.SmartConnect(**connection_params) - atexit.register(connect.Disconnect, instance) - self.session = instance.RetrieveContent() + self.session = self._sdk_instance.RetrieveContent() except vim.fault.InvalidLogin as e: log.error(f"{def_exception_text} {e.msg}") @@ -496,6 +495,21 @@ class VMWareHandler(SourceBase): return True + def finish(self): + + # closing tag session + if self._sdk_instance is not None: + try: + connect.Disconnect(self._sdk_instance) + except Exception as e: + log.error(f"unable to close vCenter SDK connection: {e}") + + # closing SDK session + try: + del self.tag_session + except Exception as e: + log.error(f"unable to close vCenter API instance connection: {e}") + def apply(self): """ Main source handler method. This method is called for each source from "main" program diff --git a/netbox-sync.py b/netbox-sync.py index 89b47bb..6387af8 100755 --- a/netbox-sync.py +++ b/netbox-sync.py @@ -137,6 +137,14 @@ def main(): # prune orphaned objects from NetBox nb_handler.prune_data() + # loop over sources and patch netbox data + for source in sources: + # closing all open connections + source.finish() + + # closing NetBox connection + nb_handler.finish() + # finish log.info("Completed NetBox Sync in %s" % get_relative_time(datetime.now() - start_time))